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

D

David Portabella

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
 
O

Owen Jacobson

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
 
J

Joshua Cranmer

David said:
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.
 
D

David Portabella

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
 
P

public boolean

David said:
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.
 
M

Mark Space

David said:
+++++++++++++++++++++++++++++++++++
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 said:
// 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.
 
L

Lew

Mark said:
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/.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top