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" ];
クラス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;
}
}
解決
クラスの実装
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) {
// ...
}
ピーター・ローリーが言うように、キャストは文字列を整数に変換しません。
あなたの豆が標準の豆規則(つまり、ゲッターとセッターがある)に従う場合、あなたは使用できます beanutils. 。 beanutilsはいくつかの標準変換を行い、さらに追加することができます コンバーター.
次の例を参照してください。
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
上記の例にはセッターのみがありますが、それでも機能していることに注意してください。