Reputation: 90150
Following up on How to use TypeToken to get type parameter?, it seems that if I instantiate a class using a factory method then TypeToken
is no longer able to capture generic type parameters.
import com.google.common.reflect.TypeToken;
public class Test<E extends Enum<E>> {
private static enum MyEnum {
FIRST,
SECOND
};
private final TypeToken<E> enumType = new TypeToken<E>(getClass()) {
};
public static void main(String[] args) {
Test<MyEnum> container = new Test<MyEnum>() {
};
System.out.println("constructor: " + container.enumType.getRawType());
System.out.println("factory : " + build(MyEnum.class).enumType.getRawType());
}
public static <E extends Enum<E>> Test<E> build(Class<E> type) {
return new Test<E>() {
};
}
}
The above example outputs:
constructor: class Test$MyEnum
factory : class java.lang.Enum
Why doesn't this work and can it be fixed?
Upvotes: 1
Views: 461
Reputation: 5568
Depending on how the generated type should be used, another option is actually implement a ParameterizedType. Thats basically what the TypeToken.getType() would return.
public static ParameterizedType parameterizedType(Class<?> rawType, Type... actualTypeArguments) {
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return actualTypeArguments;
}
@Override
public Class<?> getRawType() {
return rawType;
}
@Override
public Type getOwnerType() {
return null;
}
};
}
Upvotes: 0
Reputation: 90150
Seeing as no one bothered converting their comment into a formal answer, I'll go ahead and do so:
As previously discussed, Java only retains information about type-parameters of generic superclasses. Since the type-parameter is associated with a method (as opposed to a superclass) the runtime value of <E>
is not retained. Replacing the factory method with
public static Test<MyEnum> of() {
return new Test<MyEnum>() {
};
}
will result in the right value, but obviously this defeats the purpose of the factory method because we're forced to use a hard-coded enum type.
To recap, TypeToken
won't work here. The only way to retain information about the type-parameter is to pass a Class<E>
as follows:
public class Test<E extends Enum<E>> {
private static enum MyEnum {
FIRST,
SECOND
};
private final Class<E> type;
public Test(Class<E> type) {
this.type = type;
}
public static void main(String[] args) {
Test<MyEnum> container = new Test<>(MyEnum.class);
System.out.println(container.type);
System.out.println(of(MyEnum.class).type);
}
public static <E extends Enum<E>> Test<E> of(Class<E> type) {
return new Test<>(type);
}
}
Upvotes: 1