简体   繁体   English

通过反射在Java中投射对象

[英]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. 因此,基本上,我将传入一个包含所有对象的数据结果数组和一个类类型T,该类需要将数组中的数据转换为给定类中的类型,并创建一个新的类型T并返回它。 The String[] fields is the field names corresponding to the data in Object[] result. String[]字段是与Object[]结果中的数据相对应的字段名称。 The field names will correspond to the Class T . 字段名称将对应于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 = 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. 使用int除非您希望null为有效选项。 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. 对于文本,我通常根据上下文使用空字符串或空白字段表示nullNaN

The problem with this, is that in Java it's unable to fetch the parameter names of a constructor. 这样做的问题是,在Java中,它无法获取构造函数的参数名称。

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. 但是我认为注释构造函数,然后在deserialize方法中获取注释信息会容易得多。 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. 正如彼得·劳里(Peter Lawrey)所说,强制转换不会将字符串转换为整数。

If your bean follows the standard bean conventions (ie you have getters & setters), then you can use BeanUtils . 如果您的bean遵循标准的bean约定(即您具有getter和setters),则可以使用BeanUtils BeanUtils does some standard conversions, and you can add more by adding a Convertor . BeanUtils进行一些标准转换,您可以通过添加Converter来添加更多转换

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. 请注意,以上示例仅具有二传手,但仍然有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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