I recently started writing a generic object mapper for a project and ran into something I don't quite understand. Given the following:
public class G<X> {
public G(Class<X> c) { }
public void m(X x) { }
public static <T> G<T> create(Class<T> c) {
return new G<T>(c);
}
public static void main(String[] args) {
Object o = ""; // irrelevant!
G<?> t = create(o.getClass());
t.m(o);
}
}
I get the following compilation error:
m(capture#402 of ?) in G<capture#402 of ?> cannot be applied to (java.lang.Object)
I can't seem to figure out a way to properly cast t
to make this compile. What am I missing? Using JDK 1.6.
EDIT:
This is not an academic question. I'm trying to write a mapper from hibernate objects to their corresponding DTO to be passed around in the REST layer. The assumption is that for each ORM object Foo
, there might exist a class FooDTO
that has a constructor that takes an instance of Foo
as a parameter. The generic class that maps Foo
to FooDTO
will encapsulate this assumption and throw appropriate exceptions if FooDTO
doesn't exist or doesn't have the proper constructor:
class Mapper<Foo,FooDTO> {
private final Constructor<FooDTO> dtoConstructor;
Mapper(Class<Foo> fooClass, Class<FooDTO> fooDTOClass){
// find the constructor of FooDTO or throw ...
}
public FooDTO map(Foo f){
return dtoConstructor.newInstance(f);
}
// this factory is for convenience when we don't know the type of FooDTO:
public static Mapper<X,Object> create(Class<X> fromClass){
Class<Object> dtoClass = // ... find it
return new Mapper<X,Object>(fromClass,dtoClass);
}
}
This seems to break if I pass a generic object class to create
.
Note that my actual implementation has all FooDTO
classes extends from a generic super class, i.e., the signature of Mapper
is actually something like Mapper<Foo,DTO<Foo>>
. I don't think that's relevant here.
EDIT 2:
Actually the suggestion of changing the line G<?> t = create(o.getClass());
to G<Object> t = (G<Object>) create(o.getClass());
worked in this context.
Unfortunately I didn't realize that the fact that my class is more complex actually has an impact. Here's a more complete example (I apologize for the piecemeal question):
public class Y<T> {
}
public class G<X, Z extends Y<X>> {
public G(Class<X> c, Class<Z> s) {
}
public void m(X x) {
}
public static <T, S extends Y<T>> G<T, S> create(Class<T> c) {
Class<S> s = null; // find this via some reflection magic
return new G<T, S>(c, s);
}
public static void main(String[] args) {
Object o = ""; // irrelevant!
G<? extends Object, Y<? extends Object>> t = create(o.getClass());
t.m(o);
}
}
In this case the object Class<S>
is created using reflection and some conventional location for objects of that type. That part works fine and should be irrelevant to this discussion. The error I am getting now is the following:
inconvertible types
found : G<capture#155 of ? extends java.lang.Object,Y<capture#155 of ? extends java.lang.Object>>
required: G<java.lang.Object,Y<java.lang.Object>>
And if I change the incriminated line to:
G<Object, Y<Object>> t = (G<Object, Y<Object>>) create(o.getClass());
I get a similar error:
java: inconvertible types
required: G<java.lang.Object,Y<java.lang.Object>>
found: G<capture#1 of ? extends java.lang.Object,Y<capture#1 of ? extends java.lang.Object>>
Once again, I apologize for the piecemeal information. I am sorting through this while I am writing.
You have passed the Class
object from the getClass()
method, which returns a Class<?>
, meaning that you had to declare t
to be a G<?>
.
You cannot call a method with a generic type parameter when the generic type parameter of the variable is a wildcard. The compiler doesn't know which specific class the wildcard really is, so it cannot guarantee type safety when such a method is called. It's the same reason that add
can't be called on a List<?>
.
To get this to compile, you must use a class literal, to avoid having a Class<?>
, and declare t
not to have a wildcard.
G<Object> t = create(Object.class);
Then
t.mo(o);
will compile.
What you have here is a consumer. However, the following seems to compile (in Eclipse).
public static class G<X, Z extends Y<X>> {
public G(Class<? extends X> c, Class<Z> s) {}
public void m(X x) {}
public static <T, S extends Y<T>> G<T, S> create(Class<? extends T> c) {
Class<S> s = null; // find this via some reflection magic
return new G<T, S>(c, s);
}
public static void main(String[] args) {
Object o = ""; // irrelevant!
create(o.getClass()).m(o);
}
}
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