I still have trouble with some corner cases in the java generics system.
I have this method (I'm only interested in the signature) :
interface Extractor<RETURN_TYPE> {
public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
}
(think about an interface whose implementations sometimes extracts an EnumSet sometimes an implementation extract a JComboBox etc.)
and I want to call it with a class obtained at runtime, so I simply call it this way :
public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
and I get a strange error message :
incompatible types
found : java.lang.Object
required: RETURN_TYPE
the location of the message if just after the opening braket of the call, before the "t" of type.
if I call it from a non-generic context, it works :
Integer extractField(final Extractor<Integer> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
Does anybody have an explanation and a solution to this problem please ?
Here is a complete file for people wanting to play with it :
public class Blah {
interface Extractor<RETURN_TYPE> {
public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
}
public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
public static Integer extractField(final Extractor<Integer> extractor, final Field field) {
final Class<?> type = field.getType();
if (type.isEnum())
return extractor.extractEnum(type.asSubclass(Enum.class));
throw new RuntimeException("the rest of the visitor is not necessary here");
}
}
thanks in advance,
Nico
I would not be surprised if this is a bug in your compiler, actually. Through serious use of generics (the kind of thing you're doing, combining parameterised methods, bounded wildcards and other "advanced" uses of generics) I've encountered two or three issues in the last year in javac (annoyingly, the same unit often compiled fine in the IDE).
In your case I'm fairly sure it's a bug, since the part that the compiler is complaining about is that extractor.extractEnum is returning an Object rather than a RETURN_TYPE. And regardless of what crazy inference it does with your enum method arguments... it knows from the type signature that the Extractor is an Extractor<RETURN_TYPE>, so you should always be able to say return extractor.extractEnum(...);.
The damning evidence is that even if you call the method with a null argument (thus completely removing any potential complications from the enum generics in the argument), the compiler still complains. In particular, it now says that it thinks the return type from the Extractor is U<RETURN_TYPE> which is clearly rubbish.
In general the solution to working around these issues is throwing in some explicit casts. Is the compiler happy if you cast the output of extractEnum to RETURN_TYPE? Edit: no, it's really not - it complains that U<RETURN_TYPE> and RETURN_TYPE are inconvertible - eep...
If you're using a recent 1.6 compiler, I suggest you report this to Sun as this is quite a big problem with javac. Here's a very short test case that exercises it:
public class Test {
interface Sub<O> {
public <I extends Enum<I>> O method(final Class<I> enumType);
}
public static <O> O go(final Sub<O> sub) {
return sub.method(null);
}
}
P.S. it's general convention to use a single uppercase letter to designate generic type parameters. I'm not going to say "I'm right, you're wrong", but bear in mind that I found your code much harder to read and follow than if you had used Extractor instead. (And judging by Hemal's phrasing of his answer it's the same for him too.)
I haven't been able to infer the original problem.
Am I correct that Extractor.extract has two type parameters, U which must be an Enum and T which is an arbitrary type? In the generic call, VV is both T and U? If U is VV than the parameter has to be Class<VV>, not Class<Enum>. The following compiles for me, but as you can see the generic method needs to be provides instance of Class<VV>
class Outer {
static class Extractor<T> {
public <U extends Enum<U>> T extract(final Class<U> lala) {
return null;
}
// two type parameters, T and U
// U must be an enum
// T is arbitrary class
}
static <VV extends Enum<VV>> VV extract(final Extractor<VV> extractor, Class<VV> vvClass) {
final Class<?> type = null;
return extractor.extract(vvClass);
// Outer.extract returns VV
// T -> VV
// it seems VV is also U
}
}
Looks like your Field.getType( ) will only return a generic Class. Because you will try to put a type with already erased type information this function will have to emit an "unchecked" warning, and ALL type information on generic interface WILL get erased.
Remember, that with type erasure your interface looks like this:
interface Extractor {
public Object extractEnum( final Class enumType );
}
So, because all type information is erased, the return type of extractEnum is java.lang.Object, so you have to add a specific cast. And this is precisely the error message that you've got.
Here is the modified example of your code.
#SuppressWarnings( "unchecked" )
public static <RETURN_TYPE> RETURN_TYPE extractField(
final Extractor<RETURN_TYPE> extractor,
final Field field
)
{
final Class type = field.getType(); // unchecked
if (type.isEnum())
{
// needs cast
return (RETURN_TYPE) extractor.extractEnum( type ); // unchecked
}
throw new RuntimeException("the rest of the visitor is not necessary here");
}
DISREGARD THIS COMMENT: To answher the original question on why you have a compile error. This is a square peg into a round hole kind of problem. type.asSubclass( Enum.class ) returns Class< ? extends Enum >, it is still not the same as Class< U > which the interface call expects.
Related
Why does this throw an error while I build the project (but not while running unittests)...
protected <E extends Enum<E>> E getEnum(JSONObject jsonObject, String propertyName, Type type)
{
String jsonString = jsonObject.optString(propertyName, null);
return new GsonBuilder().create().fromJson(jsonString, type);
}
...while this perfectly works (note the difference - last parameter - which is unused!):
protected <E extends Enum<E>> E getEnum(JSONObject jsonObject, String propertyName, Type type, Class<E> clazz)
{
String jsonString = jsonObject.optString(propertyName, null);
return new GsonBuilder().create().fromJson(jsonString, type);
}
The error:
warning: [options] bootstrap class path not set in conjunction with -source 1.7 C:\Projects\bla\bla\bla.java:32: error: incompatible types: inference variable E#1 has incompatible upper bounds Enum<E#2>,Termination
Termination termination = getEnum(jsonObject, "termination", Termination.TYPE);
where E#1,E#2 are type-variables:
E#1 extends Enum<E#1> declared in method <E#1>getEnum(JSONObject,String,Type)
E#2 extends Termination
What should I do to improve this?
Edit:
As additional info: this is how I call the method (using the 2nd example, 1st example is already shown in the error message):
Termination termination = getEnum(jsonObject, "termination", Termination.TYPE, Termination.class).
And this is the simplified version of that enum:
public enum Termination
{
#SerializedName("endDate")END_DATE,
#SerializedName("recurrenceCount")COUNT,
#SerializedName("forever")FOREVER;
public static final java.lang.reflect.Type TYPE = new TypeToken<Termination>(){}.getType();
}
Now I understand that - due to type inference - I apparently need to define the class type as shown in the 2nd example. However, that's not my question. I'm wondering 1: why is the Gson library able to do exactly the same as I do (as far as I can see from code-examples below), and 2: why doesn't this compile in most cases, while running unittests is no problem.
Gson example code (the method called in both examples):
#SuppressWarnings("unchecked")
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
StringReader reader = new StringReader(json);
T target = (T) fromJson(reader, typeOfT);
return target;
}
Edit 2:
Apparently when I leave out the 'extends Enum' part the compiler doesn't complain anymore, so I don't need to pass the type as parameter. (this looks more like the Gson example, which was the reason why it compiled for that code but didn't compile for me in 1st example). So my first example now becomes:
protected <E> E getEnum(JSONObject jsonObject, String propertyName, Type type)
{
String jsonString = jsonObject.optString(propertyName, null);
return new GsonBuilder().create().fromJson(jsonString, type);
}
Of course I'd still like to extends E to be sure the method can only be used to return enums.
Questions remaining:
how can I improve the code to solve this?
Why does this work without passing the concrete type as parameter, and why doesn't it work while extending Enum?
Why doesn't the compiler complain for the original 1st example when running unittests?
The type parameter in the first example has no way to know what concrete type to substitute. There must be information in the argument list to resolve that. In the second example the Class<E> argument supplies the necessary information.
I have couple of enums implementing some common interface and I would like to return class literal from the method. However I am unable to specify the intersection type correctly. See below the code sample illustrating the problem.
public class GenericsTest {
interface Iface {
}
enum E1 implements Iface {
}
enum E2 implements Iface {
}
<E extends Enum<E> & Iface> Class<E> getEnum1() {
return E1.class; //ERROR incompatible types: java.lang.Class<GenericsTest.E1> cannot be converted to java.lang.Class<E>
}
Class<? extends Enum<?>> getEnum3() {
return E1.class; //OK
}
Class<? extends Iface> getEnum4() {
return E1.class; //OK
}
<E extends Enum<E> & Iface> void enumParam(Class<E> p) {
enumParam(E1.class); //OK
}
}
#getEnum1 method doesn't compile. Interestingly it works as parameter value in #enumParam. How can I specify the intersection type to be able to return class literal from the method?
The method getEnum1 does not compile for literally the same reason as this one:
public <E> E foo() {
return "bar"; // Compile time error: Incompatible types E and String
}
See? Just because the type parameter E could be eventually a string, it does not mean it is compatible with String. One could call this function as Integer i = x.<Integer>foo();, so returning a string does not make sense here.
getEnum1 is quite the same. Type parameter E could be choosen to be the type E1 (very bad naming here), but it does not mean E1 is always compatible with E. Consider E2 e2 = x.<E2>getEnum1();. You may wonder why the compiler doesn't just alert an error when one tries to call it with the wrong type parameter. The thing is, E2 satisfies the type constraints, so there is just no reason to show an error there.
So how can you make your code working?
Easiest (and possibly the only) way is to just get rid of generics there:
Class<E1> getEnum1() {
return E1.class;
}
If you don't want to expose the concrete type E1 here, you are out of luck. All you can do is to extract the common functionalities to an interface, and return that type.
The problem here is that there is no way your code would be guaranteed to return the right Class type instance given the context in which is use each time.
E.g.
final Class<E2> e2var = new GenericsTest().getEnum1();
The compiler fails at getEnum1 to prevent a delayed run-time error when someone tries to do some E2 bound operation on the object referred by e2var which is in fact always Class<E1>.
To propose a solution you need to be more specific of what is the intended use of this class set.
In contrast is not surprising that enumParam works as it does not need to return a matching class object. Yet, it is obvious that it would cause a stack-overflow due to an infinite recursion.
public class Foo<T extends Bar>{
private Class<T> _type;
public Foo( Class<T> _type ){
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ){ //PROBLEMATIC
return dostuffWithItems( items );
}
}
Usage:
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
List<ChildBar> items = ( List<ChildBar> ) foo.hypothetical( new ArrayList<ChildBar>() ); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>)
The compiler would either accept
casting List<ChildBar> items argument to List<?>
or changing the hypothetical( List<T> items ) signature to either
a) hypothetical( List<ChildBar> items ) or
b) hypothetical( List<? extends Bar> items )
However, none of the alternatives assure that the hypothetical method's List items argument T type is the equivalent runtime type of the Foo class T parametric type. I am currently using an extra method to verify the parametric types at the moment.
Is there a better way within Java generics constructs to achieve this automatically without the extra logic? Or better yet, why can I not declare foo as Foo<? extends Bar> and then fill in the actual type parameter at runtime?
I edited your code and added the missing stuff to make it compilable, and I can confirm that the only problematic parts are:
The missing dostuffWithItems method.
The typos with the hypothetical method name.
Assigning a Collection<ChildBar> to a List<ChildBar>.
The first two are easy to fix.
The last one requires you to either change the change the API method, or change the code where you are calling it. Neither of these is (IMO) problematic. Furthermore, the
It is worth noting that you would get all of these errors if the types were non-generic. You can't assign a Collection to a List without a typecast.
Here's my code for you to play with. (Copy and paste into appropriately named files ...)
public class Bar {
}
public class ChildBar extends Bar {
}
import java.util.*;
public class Foo<T extends Bar> {
private Class<T> _type;
public Foo( Class<T> _type ) {
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ) {
return items; // dummy implementation ...
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
Collection<ChildBar> items =
foo.hypothetical( new ArrayList<ChildBar>() );
}
}
The accepted answer doesn't precisely explain why the snippet in the question (after edits) is rejected by the compiler.
We start from the observation that the snippet from #Stephen C's answer is accepted, while revision 8 of the question is rejected. The difference is: in the latter version the variable foo is declared with a wildcard-parameterized type Foo<? extends ChildBar>, while Stephen C had copied Foo<ChildBar> from an earlier revision (we all seem to agree that this is a suitable way to resolve the compile error).
To understand why this difference is crucial please see that with Foo<? extends ChildBar> foo this wildcard propagates as a capture into the signature for the invocation of foo.hypothetical, so this invocation is rendered as hypothetical(List<capture#2-of ?>), meaning that the parameter has an unknown (upper-bounded) type parameter. List<ChildBar> is not compatible to that type, hence the compile error.
Also note that all mentions of "runtime" in this thread are inappropriate, all this is statically resolved at compile time. Perhaps you meant invocation type, or type of the actual argument, as opposed to the declared type (of the formal parameter). The actual runtime type is unknown to the compiler.
This seems to be currently impossible in Java.
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
This leave foo with an ambiguous parametric type. It is obvious that ChildBar would become the true de facto parametric type. The call to the foo.hypothetical() method with the List<ChildBar> exposes this assumption to be untrue. Although foo.hypothetical only accepts a List<> argument containing elements of the foo parametric type, it still fails to recognize that the argument was a list of ChildBar objects.
For this use case, the object parametric type must be specified during foo declaration in order make it a part and parcel of the foo runtime reference.
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
All conforming List<ChildBar> arguments of the foo.hypothetical method will now correctly be accepted as carrying elements of the foo's declared parametric type.
I am working on a class that will have functionality similar to JTable's setDefaultRenderer method. I want to have Class-specific formatters which convert objects to strings suitable for displaying.
public interface Formatter<T> {
String format(T value);
}
private Map<Class<?>, Formatter<?>> formatters;
public <T> void addFormatter(Formatter<T> formatter) {
formatters.put(T.class, formatter);
}
That's the code I have right now, but Java doesn't accept T.class.
error: cannot select from a type variable
formatters.put(T.class, formatter);
^
1 error
Is there a way to write this without passing a separate Class<T> parameter? I'm trying to avoid that. It seems redundant.
No, it's impossible because of generic type erasure in Java. All information about a generic class is lost at runtime, the only solution is to explicitly pass around the class as a parameter.
Not a solution but a hack.
You can do it. But through dirty tricks and reflection. Look at below code for example. Courtesy here:
class ParameterizedTest<T> {
/**
* #return the type parameter to our generic base class
*/
#SuppressWarnings("unchecked")
protected final Class<T> determineTypeParameter() {
Class<?> specificClass = this.getClass();
Type genericSuperclass = specificClass.getGenericSuperclass();
while (!(genericSuperclass instanceof ParameterizedType) && specificClass != ParameterizedTest.class) {
specificClass = specificClass.getSuperclass();
genericSuperclass = specificClass.getGenericSuperclass();
}
final ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
final Type firstTypeParameter = parameterizedType.getActualTypeArguments()[0];
return (Class<T>) firstTypeParameter;
}
}
//change the type of PrameterizedTest<Integer> to Parameterized<String> or something to display different output
public class Test extends ParameterizedTest<Integer>{
public static void main(String... args){
Test test = new Test();
System.out.println(test.determineTypeParameter());
}
}
Here on the runtime, you get the Type Parameter. So instead in your class, you will have to define a Class object which gets the class as explained above. Then using Class.newInstance you get a new Object. But you will have to manually handle type cast and so on.
The question is: Is all this worth it??
No according to me as most of it can be avoided by using bounds in generic types and interfacing to the bound type. So you should be looking for alternative solution
In general it's not possible. In your case, though, it might be reasonable to have Formatter implement a Class<T> getFormatterTargetClass() method, which you could then use instead of T.class in your code.
Due to type-erasure T.class is not available at runtime, so you would have to pass in a separate parameter. In general, generic type-information is not available at runtime (unless you are using unbounded wildcards).
That's not possible in Java language. All the generic types are determited at compilation time, so you can't access class value in execution. So you need to pass the Class<T> parameter as well in order to be able to access it.
public Class MyGenericClassContainer<T>{
public T instance;
public Class<T> clazz;
public MyGenericClassContainer(Class<T> clazz){
intance = clazz.newInstance();
}
}
An instructive exercise is to ask yourself, "How would I do this without generics?" The answer to that is also the answer to your question.
A program with generics can be written into an equivalent program without generics, by removing generics and inserting casts in the right places, without changing anything else in the code. This transformation is called "type erasure". This means that if something cannot be written without generics, then it cannot be written with generics either.
Without generics, your code looks like this:
public interface Formatter {
String format(Object value);
}
private Map formatters;
public void addFormatter(Formatter formatter) {
formatters.put(?, formatter);
}
So, I ask you, how would you do it?
Consider the following code:
public class Generics {
C c; // initialized at runtime
public void testGenericsCall(Object o) {
c.myMethod(o);
}
}
class C<E> {
public void myMethod(E input) {
}
}
This is working, but I get warnings because the parametrized class C is used with a raw type. I cannot use a declaration like
C<String> c;
because the type of C is known only at runtime. I also cannot add a type parameter to the class Generics because I need to create objects of this class before knowing the type of C. The declaration
C<?> c;
or
C<? extends Object> c;
would be OK for the compiler, but then the method testGenericsCall does not compile ("actual argument java.lang.Object cannot be converted to capture#1 of ? by method invocation conversion")
What is the best way to deal with a situation like this?
EDIT: Note that when I actually (at runtime) create an instance of C, I know its type parameter, this part of the code is type-safe and working well. In the real code, I don't have a single "C" class, but a series of interrelated classes, and there the generics are definitely useful (even if in this simplified example this is not obvious - so please don't just tell me not to use generics :). I already have the compile-time type-safety, but not here, but between C and other classes (not shown here).
I see how in this case I cannot check the type parameter at compile time, that's why I tried to declare it C<?> c. Here I am just looking for the best way to bridge the generic and not-generic code without compiler warnings.
Because of type erasure, there's no way to use generics at runtime. You'll have to deal with your data type programmatically, by checking type or anything (reflection maybe).
You can do it. But through dirty tricks and reflection. Look at below code for example. Courtesy here:
class ParameterizedTest<T> {
/**
* #return the type parameter to our generic base class
*/
#SuppressWarnings("unchecked")
protected final Class<T> determineTypeParameter() {
Class<?> specificClass = this.getClass();
Type genericSuperclass = specificClass.getGenericSuperclass();
while (!(genericSuperclass instanceof ParameterizedType) && specificClass != ParameterizedTest.class) {
specificClass = specificClass.getSuperclass();
genericSuperclass = specificClass.getGenericSuperclass();
}
final ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
final Type firstTypeParameter = parameterizedType.getActualTypeArguments()[0];
return (Class<T>) firstTypeParameter;
}
}
//change the type of PrameterizedTest<Integer> to Parameterized<String> or something to display different output
public class Test extends ParameterizedTest<Integer>{
public static void main(String... args){
Test test = new Test();
System.out.println(test.determineTypeParameter());
}
}
Here on the runtime, you get the Type Parameter. So instead in your class, you will have to define a Class object which gets the class as explained above. Then using Class.newInstance you get a new Object. But you will have to manually handle type cast and so on.
The question is: Is all this worth it??
No according to me as most of it can be avoided by using bounds in generic types and interfacing to the bound type