views:

685

answers:

5

I have been trying to determine the type of a field in a class. I've seen all the introspection methods but haven't quite figured out how to do it. This is going to be used to generate xml/json from a java class. I've looked at a number of the questions here but haven't found exactly what I need.

Example:

class Person {
    public final String name;
    public final List<Person> children;
}

When I marshall this object, I need to know that the chidren field is a list of objects of type Person, so I can marshall it properly.

Thanks

+8  A: 

Have a look at Obtaining Field Types from the Java Tutorial Trail: The Reflection API.

Basically, what you need to do is to get all java.lang.reflect.Field of your class and call Field#getType() on each of them (check edit below). To get all object fields including public, protected, package and private access fields, simply use Class.getDeclaredFields(). Something like this:

for (Field field : Person.class.getDeclaredFields()) {
    System.out.format("Type: %s%n", field.getType());
    System.out.format("GenericType: %s%n", field.getGenericType());
}

EDIT: As pointed out by wowest in a comment, you actually need to call Field#getGenericType(), check if the returned Type is a ParameterizedType and then grab the parameters accordingly. Use ParameterizedType#getRawType() and ParameterizedType#getActualTypeArgument() to get the raw type and an array of the types argument of a ParameterizedType respectively. The following code demonstrates this:

for (Field field : Person.class.getDeclaredFields()) {
    System.out.print("Field: " + field.getName() + " - ");
    Type type = field.getGenericType();
    if (type instanceof ParameterizedType) {
        ParameterizedType pType = (ParameterizedType)type;
        System.out.print("Raw type: " + pType.getRawType() + " - ");
        System.out.println("Type args: " + pType.getActualTypeArguments()[0]);
    } else {
        System.out.println("Type: " + field.getType());
    }
}

And would output:

Field: name - Type: class java.lang.String
Field: children - Raw type: interface java.util.List - Type args: class foo.Person
Pascal Thivent
Pascal -- you're super close. you want Type type = Field.getGenericType();And then check if it's a ParameterizedType, then grab the parameters. This ends up being a rabbit hole -- asker needs to define limits or be prepared to write some pretty fancy code.
wowest
Thanks wowest, that is exactly what I need in conjunction with ParameterizedType.getRawType and ParameterizedType.getActualArguments.
Juan Mendes
I can't figure out how to mark your comment as the right answer so I'll just answer my own question so I can post an example
Juan Mendes
@Juan Mendes, you should be seeing a tick icon to the left of each of the answers, next to the vote number, just click on the one that belongs to the correct answer so it turns green.
Iker Jimenez
I knew that I could mark this answer as the right answer. However, it was not correct until Pascal edited it to agree with wowest's comment. In any case, the example I listed below contains all the info I need but I don't want to mark my own answer as the right answer after so many people helped me. Pascal's example is still missing critical methods (ParameterizedType.getRawType and ParameterizedType.getActualArguments?).Pascal, could you edit the example so it shows those two methods being used?
Juan Mendes
@Juan I initially didn't wanted to provide all the code as you actually did the job and answered your own question entirely. But it was so kindly asked that I updated my answer (with something very similar to what you wrote, of course).
Pascal Thivent
@Pascal, This is the first time I actually posted a question here at stack overflow and found that people here are courteous and smart when asking and responding to questions. Other forums I've used are usually only one or the other :). Stack overflow will surely become my first place to ask questions and I will definitely try to answer questions as I can.
Juan Mendes
+3  A: 

take this snippet:

 for (Field field : Person.class.getFields()) {
        System.out.println(field.getType());
 }

the key class is Field

dfa
That's public fields only (including static) with `Class.getFields`. Also it'll be the erased type.
Tom Hawtin - tackline
then I misunderstood the question
dfa
+2  A: 

As dfa points out, you can get the erased type with java.lang.reflect.Field.getType. You can get the generic type with Field.getGenericType (which may have wildcards and bound generic parameters and all sorts of craziness). You can get the fields through Class.getDeclaredFields (Class.getFields will give you public fields (including those of the supertpye) - pointless). To get the base type fields, go through Class.getSuperclass. Note to check modifiers from Field.getModifiers - static fields probably will not be interesting to you.

Tom Hawtin - tackline
A: 

Here's an example that answers my question

class Person {
  public final String name;
  public final List<Person> children;  
}

//in main
Field[] fields = Person.class.getDeclaredFields();
for (Field field : fields) {
  Type type = field.getGenericType();
  System.out.println("field name: " + field.getName());
  if (type instanceof ParameterizedType) {
    ParameterizedType ptype = (ParameterizedType) type;
    ptype.getRawType();
    System.out.println("-raw type:" + ptype.getRawType());
    System.out.println("-type arg: " + ptype.getActualTypeArguments()[0]);
  } else {
    System.out.println("-field type: " + field.getType());
  }
}

This outputs

field name: name
-field type: class java.lang.String
field name: children
-raw type:interface java.util.List
-type arg: class com.blah.Person
Juan Mendes
A: 

Here is a somewhat longer explanation of Java Reflection and Generics which also covers generic method and field types:

http://tutorials.jenkov.com/java-reflection/generics.html

Jakob Jenkov
Very nice! Wish I had found your page before I posted the question.
Juan Mendes