简体   繁体   中英

Casting objects via reflection in Java

I am writing a deserializer method, which looks like so:

public <T> T deserialize(Object[] result, String[] fields, Class<T> type);

So basically I will be passed in a result array of data which is all objects, and a class type T which I need to convert the data in the array to the types in the given class, and create a new class of type T and return it. The String[] fields is the field names corresponding to the data in Object[] result. The field names will correspond to the Class T .

The casting will need to use reflection of the given class to find out the type of each field.

eg.

result = ["Mike", "London", 28];
fields = ["name", "location", "age" ];

Class T =

public class GivenClass{

  private String name;
  private String location;
  private Integer age;

  public GivenClass(String name, String location, Integer age){
    this.name = name;
    this.location = location;
    this.age = age;
  }
}

Class implementation

static class GivenClass {

    private String name;
    private String location;
    private Integer age;

    public GivenClass(String name, String location, Integer age) {
        this.name = name;
        this.location = location;
        this.age = age;
    }

    public GivenClass(Map<String, Object> data) throws Exception {
        for (Field f : GivenClass.class.getDeclaredFields())
            f.set(this, data.get(f.getName()));
    }

    public Map<String, Object> serialize() throws Exception {
        Map<String, Object> fields = new HashMap<String, Object>();
        for (Field f : GivenClass.class.getDeclaredFields()) 
            fields.put(f.getName(), f.get(this));
        return fields;
    }

    @Override
    public String toString() {
        return "age=" + age + ", location=" + location + ", name=" + name;
    }
}

Example:

public static void main(String[] args) throws Exception {

    GivenClass o1 = new GivenClass("Mike", "London", 28);

    Map<String, Object> serialized = o1.serialize();

    GivenClass o2 = new GivenClass(serialized);
    System.out.println(o2.toString());
}

Output:

age=28, location=London, name=Mike

You need to do the conversion yourself. Reflections doesn't convert (it will only check the type of an object is already correct)

Reflections won't give you the names of method/constructor parameters. (You can get them from the debug byte code but that's a real pain)

The approach I take is to use the convention that the constructor parameters are in the same order as the fields. You will also want to assume the type of constructor parameters and field types match. ;)

I would also use primitives instead of wrappers whenever possible. Use int unless you want null to be a valid option. If this is the case you should think about how you want to represent this. For text I usually use empty strings or blank field for null or NaN depending on the context.

The problem with this, is that in Java it's unable to fetch the parameter names of a constructor.

For this particular example, you'll need a default constructor, with which you could create an empty object.

public GivenClass() {
  super();
}

Then you could use reflection to get the fields of the class, and then set the appropriate value for them.

But I think it would be much easier to annotate your constructor, and then fetch the annotation informations in your deserialize method. In this case you won't need to fetch the fields and create an empty constructor.

Example:

You need to create a annotation like this:

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Property
{
    String value();
}

And then you can use it in your constructor like this:

public GivenClass(@Property("name") String name, @Property("location") String location, @Property("age") Integer age) {
  // ...
}

As Peter Lawrey says, casting does not convert a string into an integer.

If your bean follows the standard bean conventions (ie you have getters & setters), then you can use BeanUtils . BeanUtils does some standard conversions, and you can add more by adding a Convertor .

See the following example:

import org.apache.commons.beanutils.BeanUtils;

public class BeanUtilsTest {
    public static class Obj {
        private int number;
        private String string;

        public void setNumber(int number) {
            this.number = number;
        }
        public void setString(String string) {
            this.string = string;
        }

        public String toString() {
            return "number=" + number + " string=" + string;
        }
    }

    public static void main(String args[]) throws Exception {
        String[] values = new String[] { "1", "two" };
        String[] properties = new String[] { "number", "string" };

        Obj obj = new Obj();

        for (int i = 0; i < properties.length; i++) {
            BeanUtils.setProperty(obj, properties[i], values[i]);
        }

        System.out.println("obj=" + obj);
    }
}

This produces as output:

obj=number=1 string=two

Note that the above example has only setters, but still works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM