Java Forum / General / September 2006
Generics and reflection
chauvin.mathieu@gmail.com - 07 Sep 2006 10:40 GMT Hi.
I would like to find, at run-time, the actual return type of a generic method. For a parameterized return type, that's OK, but what about a TypeVariable return type ??
Suppose we have the following:
abstract class Generic<T> { private T genericInstance; public T getGeneric() { return genericInstance; } public void setGeneric(T instance) { genericInstance = instance; } }
// Recursive structure class Concrete extends Generic<Concrete> { }
The class Concrete actually defines a method which looks like: public Concrete getGeneric();
But I just can't figure out, and I don't even know if it's possible, how to find that this method returns an instance of Concrete.
Concrete.class.getMethod("getGeneric", null).getGenericReturnType() returns a TypeVariable, and I just can't see how to do from it !
Thanks !
Mathieu
hiwa - 07 Sep 2006 10:59 GMT chauvin.mathieu@gmail.com のメッセージ:
> Hi. > [quoted text clipped - 31 lines] > > Mathieu I think your code won't compile.
chauvin.mathieu@gmail.com - 07 Sep 2006 11:08 GMT You may have to change one or two things (I wrote this code by hand), but It does compile.
Well, actually, I think it's only possible to find the 'real' Type by getting the generic superclass of Concrete, which is parameterized, and then get the actual type.
Seems complicated !
hiwa a écrit :
> I think your code won't compile. Zaph0d - 07 Sep 2006 11:44 GMT Please see http://groups.google.com/group/comp.lang.java.programmer/msg/deaf0e48a4e9ae72
After reading and understanding that, I'll reiterate the gist that is relevant to you: _There is no way to determine the generic type in runtime_
What you can do is make your constructor something like this; Class A<B> { B b; A(B b) {this.b = b;} class returnGenericClass() { return b.class;} String returnGenericClassName() { return b.class.name();} //or something like that, look the api. } //A
But someone could still do: A<JComponent> = new A(JButton); and you'll get "JButton" (or something like that). (Sorry for the swing example, can't think of a simple, proper example now).
chauvin.mathieu@gmail.com - 07 Sep 2006 11:56 GMT Aren't things slightly different from your example, in that the Concrete class *is not* generic, when your A class *is* ? I think that the information that Concrete extends Generic AND that the generic T is mapped to a Concrete type IS present in bytecode and CAN be retrieved at run-time, even if it is complicated. In my exemple, we can retrieve the fact that T is actually Concrete by getting the generic superclass. It actually works, but I'm not sure that it will be as simple, or even possible, when there are many generic classes, more than one TypeParameters, ....
Piotr Kobzda - 07 Sep 2006 19:02 GMT > I think that the information that Concrete extends Generic AND that the > generic T is mapped to a Concrete type IS present in bytecode and CAN > be retrieved at run-time, even if it is complicated. Yes. You right.
> In my exemple, we can retrieve the fact that T is actually Concrete by > getting the generic superclass. It actually works, but I'm not sure > that it will be as simple, or even possible, when there are many > generic classes, more than one TypeParameters, .... For your example class it is possible, and is really simple:
((ParameterizedType) concreteClass.getGenericSuperclass()) .getActualTypeArguments()[0]
But in general, it is not easy (you have to analyze whole inheritance tree) and sometimes it is simply useless:
class Concrete2 extends Concrete {} class Concrete3<T> extends Generic<T> {} class Concrete4<S, T> extends Generic<Concrete3<T>> {} ...
So I strongly suggest to forget about this concept. :)
If you really need to know the type of your class parameters, much better solution is to directly provide class of this type parameters to all interested classes, for example:
abstract class Abstract<T extends Abstract<T>> { final protected Class<T> typeOfT; protected Abstract(Class<T> typeOfT) { this.typeOfT = typeOfT; } //... }
with concrete classes like following:
class Concrete extends Abstract<Concrete> { Concrete() { super(Concrete.class); } }
piotr
Chris Uppal - 07 Sep 2006 11:20 GMT > The class Concrete actually defines a method which looks like: > public Concrete getGeneric(); [...]
> Concrete.class.getMethod("getGeneric", null).getGenericReturnType() > returns a TypeVariable, and I just can't see how to do from it ! If you are interested in a concrete method of a concrete class, why are you using getGenericReturnType() instead of getReturnType() ?
My advice (without knowing anything at all about your application) would be to ignore generics entirely when doing reflection. Generics don't exist at runtime, and reflection is an entirely runtime concept, so generics are entirely irrelevant -- better to make your program work in terms of the /real/ types seen by the JVM, not the fake types dreamed up by the compiler.
-- chris
chauvin.mathieu@gmail.com - 07 Sep 2006 18:20 GMT I don't use getReturnType() because it can only tell me that the method returns a java.lang.Object (in my example). It can be more accurate depending on the declaration of the generic Class (class Generic<T extends SomeClass>), but it will never be the actual return type for a subclass that binds T to something 'real' (I'm not discussing an instance of the generic class, but really a 'concrete' subclass).
Chris Uppal a écrit :
> If you are interested in a concrete method of a concrete class, why are you > using getGenericReturnType() instead of getReturnType() ? [quoted text clipped - 6 lines] > > -- chris Chris Uppal - 08 Sep 2006 08:43 GMT [Please don't top-post -- it makes more work for every one else, including me]
[me:]
> > If you are interested in a concrete method of a concrete class, why are > > you using getGenericReturnType() instead of getReturnType() ?
> I don't use getReturnType() because it can only tell me that the method > returns a java.lang.Object (in my example). You said earlier:
> The class Concrete actually defines a method which looks like: > public Concrete getGeneric(); If that is true, then you shouldn't be getting a return type of Object, unless you are looking at the wrong method.
IIRC, if you iterate over the methods of Concrete then you'll see two versions of getGeneric(), one which actually overrides the one in the Generic superclass (and which will return Object), and another with identical name and signature but which returns Concrete. OTOH, if you just ask for the method with a given name and signature then the reflection machinery will attempt to hide the gross hacks the compiler has been performing and should just return the one which returns Object.
-- chris
Mathieu - 08 Sep 2006 14:36 GMT > If that is true, then you shouldn't be getting a return type of Object, unless > you are looking at the wrong method. There is only one version of getGeneric in my case. The public Concrete getGeneric() method is *implicitly* defined because my T type variable is bound to a Concrete in the definition of Concrete. I you iterate on all methods, you'll only see *one* getGeneric(), returning Object, which declaring class is Generic, not Concrete.
Actualy, *it is* possible to retrieve information from reflection, but it is simply not possible to get the actual type.... The reason is "simple": as a type variable may have many bounds, it is only possible to get the actual types (with a 's' at the end......)
With the following: public class Foo<T extends Serializable & Comparable<T>> { public T getObject() { return null; } }
What should we say about the return type of getObject ??? It is an Object ? A Serializable ? A comparable ? It returns something that is all of them !
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|