Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics compilation error - The method method(Class<capture#1-of ? extends Interface>) in the type <type> is not applicable for the arguments

Last Thursday someone at work showed me a compile error that I wasn't able to fix in a clean way and it has been bothering me ever since.

The problem is generics related and I've reconstructed a simplified version of the code that generates the compile error. The error occurs in the very last line of code shown below.

I've been looking all over the interwebs but can't seem to find a decent explanation why the Java compiler doesn't accept the code. I guess that if it were to allow the code, it would be possible the create a class cast issue in Bar.operationOnBar(), but I don't see how.

Could someone please enlighten me why this doesn't compile?

public interface Interface {
}


public class Type implements Interface {
}

public class Bar<T> {
    public Bar(Class<T> clazz) {
    }

    public void operationOnBar(Class<T> arg){
    }
}

public class Foo {
    public <T> Bar<T> bar(Class<T> clazz){
        return new Bar<T>(clazz);
    }
    public static void main(String[] args) {
        Class<? extends Interface> extendsInterfaceClazz = Type.class;
        new Foo().bar(extendsInterfaceClazz).operationOnBar(Type.class);
    }
}

Compile Error on the second line of Foo.main():

The method operationOnBar(Class<capture#1-of ? extends Interface>) in the type Bar<capture#1-of ? extends Interface> is not applicable for the arguments (Class<Type>)

Btw. I've solved it by downcasting Type.class to Class, this way the compiler is unable to see that the generic type of Class is "Type" instead of "? extends Interface".

like image 635
Basje Avatar asked Nov 05 '11 17:11

Basje


1 Answers

A little advice: when you are not sure why compiler prohibits some generic-related conversion, replace generic classes in question with List<T>. Then it would be easy to find an example that breaks type safety.

This replacement is correct since currently Java doesn't provide a way to conduct any a priory knowledge about possible behaviours of generic classes (i.e. it lacks a way to specify covariance and contravariance of generic classes in their declarations, as in C# 4 and Scala). Therefore Class<T> and List<T> are equivalent for the compiler with respect to their possible behaviours, and compiler has to prohibit conversions that can cause problems with List<T> for other generic classes as well.

In your case:

public class Bar<T> {
    private List<T> l;

    public Bar(List<T> l) {
        this.l = l;
    }

    public void operationOnBar(List<T> arg) {
        l.addAll(arg);
    }
}

List<Type1> l1 = new ArrayList<Type1>();
List<? extends Interface> l2 = l1;
List<Type2> l3 = Arrays.asList(new Type2());

new Foo().bar(l2).operationOnBar(l3);

Type1 t = l1.get(0); // Oops!
like image 129
axtavt Avatar answered Oct 23 '22 05:10

axtavt