Failing to use User object as parameter for ? extends User in Java generics

I have trouble using generics. Firstly I’ve declared

public class SymmetricIndexedRealMatrix<T> implements iIndexedMatrix<T,T, Double>{
    public Double getElement(T rowObject, T columnObject) { ... }
}

then

public class CRF<U> {
    private SymmetricIndexedRealMatrix<U> binaryEnergy;
    private Set<U> labelSet;
    public SymmetricIndexedRealMatrix<U> getBinaryEnergy() {
        return binaryEnergy;
    }
    public Set<U> getLabelSet() {
        return labelSet;
}
    ...
}

then I use such part of code

CRF<? extends User> crf = new CRF<User>(sourceGraph, targetGraph.getElements(User.class), getUnaryEnergyComputer(), getBinaryEnergyComputer()
...
for (User targetUser1 : crf.getLabelSet()) {
    for (User targetUser2 : crf.getLabelSet()) {
        System.out.print(crf.getBinaryEnergy().getElement(targetUser1, targetUser2) + " ");
    }
    System.out.println();
}

and it results in compilation error:

The method getElement(capture#10-of ? extends User, capture#10-of ? extends User) in the type SymmetricIndexedRealMatrix is not applicable for the arguments (User, User)

What’s wrong and how should it be fixed?

Answer

Suppose you have 2 sub-classes that extend UserSubUser1 and SubUser2.

The following code is possible :

CRF<? extends User> crf = new CRF<SubUser1>(...);
User targetUser1 = new SubUser2();
User targetUser2 = new SubUser2();

Now, if you try to call crf.getBinaryEnergy().getElement(targetUser1, targetUser2), as you did, it won’t work since the specific instance assigned to crf expects SubUser1 users, not SubUser2.

Even though in your actual code you used only User types (and no sub-classes), the compiler has no way of knowing the exact type of the instance that would be assigned to crf in runtime, which is why your code doesn’t compile.

CRF<User> crf = new CRF<User> (...);

will solve the error.

Leave a Reply

Your email address will not be published. Required fields are marked *