Reputation: 9406
This question is related to a previous question. The original can be solved by adding casts to do unchecked conversions. So now I have the following code:
import java.util.EnumSet;
class A {
static enum E1 {
X
}
private static <T extends Enum<T>> EnumSet<T> barEnum(Class<T> x) {
return null;
}
private static void foo1(EnumSet<E1> s, E1 e) {
EnumSet<E1> x2 = barEnum((Class<E1>)e.getClass());
}
private static void foo2(EnumSet<E1> s) {
EnumSet<E1> x = barEnum((Class<E1>)s.iterator().next().getClass());
}
}
My original intent was to write a generic method. So I generalized the method foo2()
to:
private static <E extends Enum<E>> void foo3(EnumSet<E> s) {
EnumSet<E> x = barEnum(s.iterator().next().getClass());
}
This obviously contains unchecked conversions and compiles with the appropriate warning. But I don't cast the result of getClass()
explicitly into Class<E>
. Since foo1()
is one instance of the generic method foo3()
, I expected that I need to add the cast here too. Comparing foo1()
to foo4()
...
private static void foo4(EnumSet<E1> s) {
EnumSet<E1> x = barEnum(s.iterator().next().getClass());
}
...the two are effectively similar (the main difference being the E1 e
parameter in foo1()
). However foo1()
compiles, but foo4()
does not compile. I feel that this is inconsistent. Are there any rules allowing implicit conversions for generic methods?
Upvotes: 2
Views: 668
Reputation: 33865
There are 2 things happening. First of all if you look at the javadoc for getClass
it will say:
The actual result type is
Class<? extends |X|>
where|X|
is the erasure of the static type of the expression on whichgetClass
is called.
That means that in your generic method, barEnum
is invoked with a Class<? extends Enum>
instead of a Class<? extends Enum<E>>
. Since Enum
is a raw type, this creates an unchecked invocation, which in turn means that the return type is erased (see also: Why is generic of a return type erased when there is an unchecked conversion of a method parameter in Java 8?).
So in that case barEnum
is effectively returning a EnumSet
, which is a raw type that can be converted unchecked to EnumSet<E>
.
For your non-generic method you'd have to explicitly cast the argument to a Class<E1>
, so there is no unchecked conversion of the method arguments and thus no unchecked invocation. But, that also means that the return type is not erased and the compiler can not find a T
that is valid for that call.
Upvotes: 2
Reputation: 9543
When I extract a variable for the obtained Class and let my IDE 'fix' everything it can, I get this:
private static void foo4(Iterable<E1> s) {
Class<? extends E1> aClass = s.iterator().next().getClass();
EnumSet<E1> x = barEnum(aClass);
}
As you see, aClass
is unparametrized (there is a warning on Enum
saying that it is 'raw'). Moreover it states it can be any subclass of Enum, whereas barEnum
accepts only a specific enum, and since specific enums are final
classes (they can't have subclasses) there is a conflict.
When I then change batEnum
to
private static <T extends Enum<T>> EnumSet<T> barEnum(Class<? extends T> x) {
the error goes away since now it accepts subclasses of Enum
.
Upvotes: 0
Reputation: 2691
My recommendation would be to read the excellent Java Generics FAQ: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Off the top of my head, it won't compile because static methods don't inherit the type parameters of a generic class. Then, you're trying to get runtime class information of a parametrised type, which won't work because Java uses erasure for its generics implementation.
Generics in Java are not at all easy, once you move beyond simple collections and such. This is why the FAQ is an indispensable resource.
Upvotes: 0