Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

incompatible types and fresh type-variable

Tags:

java

generics

I get the following compilation message:

[javac]   ... error: incompatible types
[javac]         exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
[javac]                                ^
[javac]   required: Holder<Class<? extends Exception>>
[javac]   found:    Holder<Class<CAP#1>>
[javac]   where CAP#1 is a fresh type-variable:
[javac]     CAP#1 extends Exception from capture of ? extends Exception
[javac] 1 error

It would seem to me that the according to the message all should be correct. CAP#1 indeed extends Exception. So how should the above message be understood? SSCCE below (initially not posted since I was hoping to understand the error message itself in the general case):

class Holder<T> {
    public T t;
    public Holder(T t) {
       this.t = t;
    }
}

public class FooMain {
    public static void main(String args[]) throws Exception {
        Holder<Class<? extends Exception>> exceptionClassHolder;
        exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
    }
}
like image 279
Marcus Junius Brutus Avatar asked Dec 12 '13 12:12

Marcus Junius Brutus


People also ask

What is incompatible Types?

The incompatible types error most often occurs when manual or explicit conversion between types is required, but it can also happen by accident when using an incorrect API, usually involving the use of an incorrect reference type or the invocation of an incorrect method with an identical or similar name.

What is capture in Java?

wildcard capture is the process of binding the value of a wildcard type to a new type variable. For example: List<?> list = ...; shuffle(list);


1 Answers

Unfortunately, the existing answers don't explain what's going on here. First, the solution is to simply specify the type argument to Holder:

Holder<Class<? extends Exception>> exceptionClassHolder;
exceptionClassHolder =
        new Holder<Class<? extends Exception>>(new Exception().getClass());

The reason your version didn't work is because new Exception().getClass() returns a Class<? extends Exception>, where ? is a wildcard capture (referred to in the compiler error message as CAP#1). Since you use the "diamond operator" with new Holder<>, the compiler infers Class<CAP#1 extends Exception> for T and so Holder<Class<CAP#1 extends Exception>> is the type of the created object.

However, this doesn't match your declared type of Holder<Class<? extends Exception>>. It uses a nested wildcard, which doesn't capture: while CAP#1 extends Exception is some specific type extending Exception, the nested ? extends Exception represents literally any type extending Exception.

And while Class<CAP#1 extends Exception> is a subtype of Class<? extends Exception>, Holder<Class<CAP#1 extends Exception>> is not a subtype of Holder<Class<? extends Exception>> because generics aren't covariant, so the assignment fails.

By manually specifying Class<? extends Exception> for T, you help the compiler avoid this "trap".

See my similar answers on these posts:

  • Java: Wildcard Types Mismatch Results in Compilation Error
  • Bounded-wildcard related compiler error
like image 178
Paul Bellora Avatar answered Oct 25 '22 13:10

Paul Bellora