Приведение объектов через отражение в Java
-
25-10-2019 - |
Вопрос
Я пишу метод десериализатора, который выглядит так:
public <T> T deserialize(Object[] result, String[] fields, Class<T> type);
Таким образом, в основном мне будет передан массив результатов данных, который представляет собой все объекты, и тип класса T, который мне нужен для преобразования данных в массиве в типы данного класса, и создания нового класса типа T и возврата это.А String[]
Поля — это имена полей, соответствующие данным в Object[]
результат.Имена полей будут соответствовать классу T
.
При кастинге необходимо будет использовать отражение данного класса, чтобы узнать тип каждого поля.
например.
result = ["Mike", "London", 28];
fields = ["name", "location", "age" ];
Класс Т =
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;
}
}
Решение
Классовая реализация
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;
}
}
Пример:
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());
}
Выход:
age=28, location=London, name=Mike
Другие советы
Вам нужно сделать обращение самостоятельно. Размышления не конвертируются (он будет только проверять тип объекта уже верен)
Размышления не дадут вам названия параметров метода/конструктора. (Вы можете получить их из байтового кода отладки, но это настоящая боль)
Подход, который я использую, заключается в том, чтобы использовать соглашение о том, что параметры конструктора находятся в том же порядке, что и поля. Вы также захотите предположить тип параметров конструктора и типы поля совпадают. ;)
Я также использовал бы примитивы вместо оберток, когда это возможно. Использовать int
Если вы не хотите null
быть действительным вариантом. Если это так, вы должны подумать о том, как вы хотите представить это. Для текста я обычно использую пустые строки или пустое поле для null
или же NaN
в зависимости от контекста.
Проблема в том, что в Java невозможно получить имена параметров конструктора.
Для этого конкретного примера вам понадобится конструктор по умолчанию, с помощью которого вы можете создать пустой объект.
public GivenClass() {
super();
}
Затем вы можете использовать отражение, чтобы получить поля класса, а затем установить для них подходящее значение.
Но я думаю, что было бы гораздо проще аннотировать ваш конструктор, а затем получить информацию об аннотации в вашем deserialize
метод.В этом случае вам не нужно будет получать поля и создавать пустой конструктор.
Пример:
Вам нужно создать такую аннотацию:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Property
{
String value();
}
И затем вы можете использовать его в своем конструкторе следующим образом:
public GivenClass(@Property("name") String name, @Property("location") String location, @Property("age") Integer age) {
// ...
}
Как говорит Питер Лоури, кастинг не преобразует строку в целое число.
Если ваш боб следует стандартным соглашениям о бобах (то есть у вас есть Getters & Setters), то вы можете использовать Beanuatils. Анкет Beanatils делает некоторые стандартные преобразования, и вы можете добавить больше, добавив Конвертор.
См. Следующий пример:
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);
}
}
Это производит как вывод:
obj=number=1 string=two
Обратите внимание, что приведенный выше пример имеет только сеттеры, но все еще работает.