Java Type Erasure Problem

2020-06-07 01:40发布

问题:

I've made an example to demonstrate my problem:

Metrical.java

public interface Metrical<T>
{
    double distance(T other);
}

Widget.java

public class Widget implements Metrical<Widget>
{
    private final double value;

    public Widget(double value) { this.value = value; }

    public double getValue() { return value; }

    public double distance(Widget other) { return Math.abs(getValue() - other.getValue()); }
}

Pair.java

public class Pair<T>
{
    private final double value;
    private final T object1, object2;

    public Pair(T object1, T object2, double value)
    {
        this.object1 = object1;
        this.object2 = object2;
        this.value = value;
    }

    public T getObject1() { return object1; }

    public T getObject2() { return object2; }

    public double getValue() { return value; }
}

Algorithm.java

import java.util.Set;

public class Algorithm<T extends Metrical<T>>
{
    public void compute(Set<T> objects)
    {

    }

    public void compute(Set<Pair<T>> pairs)
    {

    }
}

So, in Algorithm.java, Set< Pair< T >> is being seen as a Set< T > and thus I am having type erasure problems. However, is there any way I can get away with something like this without naming the methods differently? Both variants of the algorithm are meant to operate on T's, but I need to allow for different arguments. They compute the same thing, so in an effort to avoid confusion, I would rather not name them differently. Is there any way to accommodate this?

回答1:

No there isn't.

You have to remember that someone could call your method with just a vanilla Set, in which case which one would be called?

That's why you can't do it. Just like you can't do:

interface A {
  void blah(Set set);
  void blah(Set<T> set);
}

Same problem.

The type information isn't available at runtime (ie type erasure).



回答2:

Sorry, the bad news is that you cannot do this:

public class Algorithm<T extends Metrical<T>> {
    public void compute(Set<T> objects) {
    }

    public void compute(Set<Pair<T>> pairs) {
    }
}

Due to erasure, both will erase to the same signature. There is no way around this short of renaming one of the methods.



回答3:

Sadly, this is the major area where Java Generics falls down... there is just no good solution.

I've generally resorted to making a new class, with the interface as Set<Pair<T>>, but that wraps Set<Pair<T>> (without extending it, which would cause the same problem).



回答4:

I've written an article about type erasure which can be of your interest. It gives the common widely known solution and also a tricky way to circumvent the problem. I don't know if it will be relevant for you. Anyway, it contains some techniques which may be useful under certain circumstances.

See also: Using TypeTokens to retrieve generic parameters

I hope it helps.



回答5:

Use public class Widget<K, P> implements Metrical<K extends Widget<P>>.

public double distance(Widget other) {} becomes public double distance(Widget<P> other) {}