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() );
}
}
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.
wildcard capture is the process of binding the value of a wildcard type to a new type variable. For example: List<?> list = ...; shuffle(list);
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With