getClass().getField("data").getType() does not work with Javagenerics.

Discussion in 'Java' started by David Portabella, Nov 11, 2008.

  1. Hello,

    Taking this simple code:
    +++++++++++++++++++++++++++++++++++
    import java.lang.reflect.*;

    public class Test<T> extends Class1{
    public T data;

    public static void main(String[] argv) throws Exception {
    Test<String> app = new Test<String>();

    Field f = app.getClass().getField("data");
    System.out.println("type: " + f.getType());
    }
    }
    +++++++++++++++++++++++++++++++++++

    it produces: "type: class java.lang.Object" instead of "type: class
    java.lang.String".
    why??
    how can I get "java.lang.String"?

    ps: I've tried with f.getGenericType(), but it returns "T", which is
    not useful neither for my purpose.


    Regards,
    DAvid
     
    David Portabella, Nov 11, 2008
    #1
    1. Advertising

  2. On Nov 11, 12:24 pm, David Portabella <>
    wrote:
    > Hello,
    >
    > Taking this simple code:
    > +++++++++++++++++++++++++++++++++++
    > import java.lang.reflect.*;
    >
    > public class Test<T> extends Class1{
    >         public T data;
    >
    >         public static void main(String[] argv) throws Exception {
    >                 Test<String> app = new Test<String>();
    >
    >                 Field f = app.getClass().getField("data");
    >                 System.out.println("type: " + f.getType());
    >         }}
    >
    > +++++++++++++++++++++++++++++++++++
    >
    > it produces: "type: class java.lang.Object" instead of "type: class
    > java.lang.String".
    > why??
    > how can I get "java.lang.String"?


    By storing a Class<T> instance in your object somewhere. As Andrea
    said, generics in Java are implemented using erasure and a few
    synthetic methods in certain contexts. Without constraints, a
    reference of a generic type becomes a reference to Object at compile
    time. Consider, however:

    public class Test2<T> {
    // A convenience factory to reduce
    // Test2<String> = new Test2<String> (String.class)
    // to
    // Test2<String> = Test2.create (String.class)
    public static <T> Test2<T> create (Class<T> type) {
    return new Test2<T> (type);
    }

    private final Class<T> genericType;
    public Test2<T> (Class<T> type) {
    this.genericType = type;
    }

    public Class<T> getGenericType () { return genericType; }
    }

    -o
     
    Owen Jacobson, Nov 11, 2008
    #2
    1. Advertising

  3. David Portabella wrote:
    > Hello,
    >
    > Taking this simple code:
    > +++++++++++++++++++++++++++++++++++
    > import java.lang.reflect.*;
    >
    > public class Test<T> extends Class1{
    > public T data;
    >
    > public static void main(String[] argv) throws Exception {
    > Test<String> app = new Test<String>();
    >
    > Field f = app.getClass().getField("data");
    > System.out.println("type: " + f.getType());
    > }
    > }
    > +++++++++++++++++++++++++++++++++++
    >
    > it produces: "type: class java.lang.Object" instead of "type: class
    > java.lang.String".
    > why??
    > how can I get "java.lang.String"?


    Welcome to the wonderful world of erasure! In the JVM, the type of data
    actually *is* an Object. A run through javap shows that it is, in fact,
    Object:

    jcranmer@quetzalcoatl /tmp $ cat Test.java
    public class Test<T> {
    public T data;
    }
    jcranmer@quetzalcoatl /tmp $ javap Test
    Compiled from "test.java"
    class Test extends java.lang.Object{
    public java.lang.Object data;
    Test();
    }

    > ps: I've tried with f.getGenericType(), but it returns "T", which is
    > not useful neither for my purpose.


    Without reification of generics, that is as far as you can go. The class
    cannot differentiate between Test<String>, Test<? extends
    Collection<?>>, or even just plain Test since it is not mentioned at all
    in the byte code. In cases where the class is extended, it is possible
    (sometimes) to wring out a little more data, but it's not terribly
    helpful in the overarching scheme of things since it can't cover all cases.

    --
    Beware of bugs in the above code; I have only proved it correct, not
    tried it. -- Donald E. Knuth
     
    Joshua Cranmer, Nov 11, 2008
    #3
  4. On Nov 11, 6:33 pm, Andrea Francia
    <_FROM_HERE_ohohohioquestoèdatogliereohohoho_TO_HERE.it>
    wrote:
    > Because generic are implemented with type erasure, google "generic type
    > erasure".


    Thanks all for your answers!

    According to the Java tutorial, "type erasure exists so that new code
    may continue to interface with legacy code."
    http://java.sun.com/docs/books/tutorial/java/generics/erasure.html

    that's too bad!! :(


    ++++++++++++++
    public class MyClass<E> {
    public static void myMethod(Object item) {
    if (item instanceof E) { //Compiler error
    ...
    }
    E item2 = new E(); //Compiler error
    E[] iArray = new E[10]; //Compiler error
    E obj = (E)new Object(); //Unchecked cast warning
    }
    }
    ++++++++++++
    the drawbacks found on this example are still worse!!! :(



    However, with the example that I gave at the beginning,
    come on, if the compiler would be a bit intelligent,
    the class of "data" could even be resolved at compilation time!!! :(

    +++++++++++++++++++++++++++++++++++
    import java.lang.reflect.*;

    public class Test<T> extends Class1{
    public T data;

    public static void main(String[] argv) throws Exception {
    Test<String> app = new Test<String>();

    Field f = app.getClass().getField("data");
    System.out.println("type: " + f.getType());
    }
    }
    +++++++++++++++++++++++++++++++++++


    :(


    Let's hope that this is solved soon.
    Some nice article found based on Joshua Cranmer's answer:
    http://gafter.blogspot.com/2006/11/reified-generics-for-java.html


    Regards,
    DAvid
     
    David Portabella, Nov 11, 2008
    #4
  5. David Portabella wrote:
    > Hello,
    >
    > Taking this simple code:
    > +++++++++++++++++++++++++++++++++++
    > import java.lang.reflect.*;
    >
    > public class Test<T> extends Class1{
    > public T data;
    >
    > public static void main(String[] argv) throws Exception {
    > Test<String> app = new Test<String>();
    >
    > Field f = app.getClass().getField("data");
    > System.out.println("type: " + f.getType());
    > }
    > }
    > +++++++++++++++++++++++++++++++++++
    >
    > it produces: "type: class java.lang.Object" instead of "type: class
    > java.lang.String".
    > why??


    Type erasure.

    Try public class Test2 extends Test<String> and see if it knows that
    Test2's data is a String (I'm guessing no). Then put a method T
    getData() in Test and see what return type reflection thinks it has in
    Test and in Test2 (Object in Test, probably Object even in Test2). Then
    override it in Test2 with just return super.getData() and try again --
    probably Object in Test but String in Test2.

    Short answer: to keep generic type information at runtime, you need to
    either subclass the generic class with a fixed value to the type
    parameter (and perhaps override everything to call super), or pass in a
    Class<T> to the constructor that you tuck away in a field somewhere for
    the reflection code to find. (Has other uses too, for example if you
    need to cast to T and get an exception right there if the type is
    mismatched, or you need to create a new T or a new array of T.)

    There are proposals afoot to address this, but I haven't heard anything
    definite.
     
    public boolean, Nov 11, 2008
    #5
  6. David Portabella

    Mark Space Guest

    David Portabella wrote:
    >
    > +++++++++++++++++++++++++++++++++++
    > import java.lang.reflect.*;
    >
    > public class Test<T> extends Class1{
    > public T data;

    public Class<T> type;

    public Test( Class<T> t ) {
    type = t;
    }
    >
    > public static void main(String[] argv) throws Exception {
    > // Test<String> app = new Test<String>();

    Test<String> app = new Test<String>(String.class);
    >
    > // Field f = app.getClass().getField("data");
    > // System.out.println("type: " + f.getType());

    System.out.println("type: " + app.type.getName() );
    > }
    > }


    > Let's hope that this is solved soon.



    This will help. It's now illegal to make a type of Test without
    supplying the class token needed to determine the type. It might help
    with what you are trying to do. (Obviously, the fields should be private
    and accessed through getters.)

    I share some of your concerns about the current lack of reifiable
    generics. However, it's not too bad once you learn to expect it and
    plan your classes appropriately.

    Normally, if you need the type of some variable, you've done it wrong.
    All types like this should be treated polymorphically. Provide a method
    to do what you need, and override it for each of your types.

    Also I just thought of this:

    > public class Test<T> extends Class1{
    > public T data;
    >
    > public static void main(String[] argv) throws Exception {
    > Test<String> app = new Test<String>();

    app.data = "test";
    >
    > // Field f = app.getClass().getField("data");
    > // System.out.println("type: " + f.getType());

    System.out.println("type: " + app.data.getClass()
    .getName() );
    > }
    > }


    Kind of obvious really, but it's good to have sometimes.
     
    Mark Space, Nov 12, 2008
    #6
  7. David Portabella

    Lew Guest

    Mark Space wrote:
    > I share some of your concerns about the current lack of reifiable
    > generics. However, it's not too bad once you learn to expect it and
    > plan your classes appropriately.
    >
    > Normally, if you need the type of some variable, you've done it wrong.
    > All types like this should be treated polymorphically. Provide a method
    > to do what you need, and override it for each of your types.


    Joshua Bloch devotes a chapter to "Prefer interfaces to reflection" (Item 53)
    in /Effective Java/.

    --
    Lew
     
    Lew, Nov 12, 2008
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. klauern
    Replies:
    9
    Views:
    7,185
    Steve W. Jackson
    Apr 12, 2005
  2. Replies:
    1
    Views:
    874
    John C. Bollinger
    Jun 13, 2005
  3. Boki
    Replies:
    34
    Views:
    28,058
  4. Replies:
    6
    Views:
    9,447
    Viator
    Nov 24, 2005
  5. JavaEnquirer

    Generics and getClass

    JavaEnquirer, Feb 10, 2006, in forum: Java
    Replies:
    4
    Views:
    24,635
    darkaico
    May 4, 2011
Loading...

Share This Page