I have the following classes:
public class Obj<T> extends BaseModel {
public static final String OBJECT = "object";
public Obj(T object) {
setObject(object);
}
public T getObject() {
return get(OBJECT);
}
public void setObject(T object) {
set(OBJECT, object);
}
}
And...
/** This is a 3rd party library class **/
public class BaseModel implements ModelData, Serializable {
//...members and stuff...
@SuppressWarnings({"unchecked", "rawtypes"})
public <X> X get(String property) {
X obj = null;
if (start > -1 && end > -1) {
Object o = map.get(property.substring(0, start));
String p = property.substring(start + 1, end);
if (o instanceof Object[]) {
obj = (X) ((Object[]) o)[Integer.valueOf(p)];
} else if (o instanceof List) {
obj = (X) ((List) o).get(Integer.valueOf(p));
} else if (o instanceof Map) {
obj = (X) ((Map) o).get(p);
}
} else {
obj = (X) map.get(property);
}
return obj;
}
}
When I compile, I get the following error.
type parameters of <X>X cannot be determined; no unique maximal instance exists for type variable X with upper bounds T,java.lang.Object -> getObject()
It doesn't happen in Eclipse, which, as far as I can tell, is using the same JDK as my Ant build. I've seen the SO thread about the Sun compiler issue, but that seemed to be for static methods declaring types on the fly.
Why am I getting this error, and more importantly, how do I get around it?
So far the only why I've found is to cast in my method like this:
@SuppressWarnings({"unchecked"})
public T getObject() {
return (T) get(OBJECT); //yuck
}
Telling my I'm on crack and this is the proper way is acceptable.
Method type parameters are most often implicitly inferred from the arguments to that method. Note, however,
get
has no explicit relationship between the argument and the type parameter:Type inference is the usual path, but methods can also be invoked with explicit type arguments, just like classes. The format roughly follows that of the declaration, so inside of Obj you could have
You could also just be direct and use
<Object>
, but you'd still have to use that unchecked cast to get it back toT
. Note the explicit argument needs a qualifier, usually the instance name of the class. Since your example used a method of the superclass, its reference is implicit throughsuper
.This doesn't solve the underlying problem of applying a generic method (
<X> X get
) inside of a non-generic class (BaseModel
). Note the code in the library makes forcible type casts to the type argument. This style is indeed one of the solutions to back-porting generic features into non-generic Java code. It looks like they're trying to hide this from the library users, but since they didn't genericize the class the type can't be inferred from the instance (i.e. you really want to haveObj<T> extends BaseModel<T>
).[EDIT: corrected and explained explicit method type argument]
I just encountered a similar issue with a project using Apache Pivot. The client code was riddled with lines like:
The code would compile in Eclipse, but not using Maven or
javac
from the command line. It appears to be Bug 6302954, but I still see it after updating to the latest JDK.As the
JSON
class is provided by Pivot, it's not something I could modify within my own source tree (forking the library is not an option on this project)The solution that worked for me came from the first reply in the bug report, changing the code to read:
This is dummy bug that has been fixed in Java SE 7.
It does not compile because your code expects too much from generics -> i.e., the < X > X part in:
In the following code:
you have to keep in mind that generics are always "unfolded" before the compiler actually starts to compile the Java code. It is a pre-processing step.
In your case, the compiler does not know what to use to replace X at compile time. The compiler needs to be sure about the type of X, because it needs to check it against T to validate the code. Hence the error...
A solution to your issue is to replace < X > X with Object:
and add a cast in:
Your will get an unchecked-cast warning at compile time, but your code will compile (so yes your workaround is valid).