初心者Java質問。私が持っているとしましょう:
public class Car{
...
}
public class Truck extends Car{
...
}
すでにCarオブジェクトがあると仮定します。このCarオブジェクトから新しいTruckオブジェクトを作成して、Carオブジェクトのすべての値を新しいTruckオブジェクトにコピーするにはどうすればよいですか?理想的には私はこのようなことをすることができます:
Car c = new Car();
/* ... c gets populated */
Truck t = new Truck(c);
/* would like t to have all of c's values */
独自のコピーコンストラクタを作成する必要がありますか?これは、Carが新しいフィールドを取得するたびに更新する必要があります...
はい、コンストラクタをトラックに追加するだけです。必ずしもパブリックではありませんが、おそらくCarにもコンストラクタを追加する必要があります。
public class Car {
protected Car(Car orig) {
...
}
public class Truck extends Car {
public Truck(Car orig) {
super(orig);
}
...
}
原則として、クラスは葉(およびそれらを最終としてマークすることもできます)または抽象のいずれかにすることが一般的に最善です。
Car
オブジェクトが必要で、同じインスタンスをTruck
に変換するように見えます。これを行うより良い方法は、Car
(Vehicle
)内の別のオブジェクトに動作を委任することです。そう:
public final class Vehicle {
private VehicleBehaviour behaviour = VehicleBehaviour.CAR;
public void becomeTruck() {
this.behaviour = VehicleBehaviour.TRUCK;
}
...
}
Cloneable
を実装すると、オブジェクトを同じクラスのインスタンスに「自動的に」コピーできます。ただし、それには、エラーが発生しやすく、finalの使用を禁止する変更可能なオブジェクトの各フィールドをコピーする必要があるなど、いくつかの問題があります。
プロジェクトでSpringを使用している場合は、ReflectionUtilsを使用できます。
独自のコピーコンストラクタを作成する必要がありますか?これは、Carが新しいフィールドを取得するたびに更新する必要があります...
どういたしまして!
この方法を試してください:
public class Car{
...
}
public class Truck extends Car{
...
public Truck(Car car){
copyFields(car, this);
}
}
public static void copyFields(Object source, Object target) {
Field[] fieldsSource = source.getClass().getFields();
Field[] fieldsTarget = target.getClass().getFields();
for (Field fieldTarget : fieldsTarget)
{
for (Field fieldSource : fieldsSource)
{
if (fieldTarget.getName().equals(fieldSource.getName()))
{
try
{
fieldTarget.set(target, fieldSource.get(source));
}
catch (SecurityException e)
{
}
catch (IllegalArgumentException e)
{
}
catch (IllegalAccessException e)
{
}
break;
}
}
}
}
はい、手動で行う必要があります。また、コピーを「深く」コピーする方法も決定する必要があります。たとえば、Carがタイヤのコレクションを持っているとします。shallowコレクションのコピーを実行できます(元のオブジェクトがコレクションのコンテンツを変更した場合、新しいオブジェクトも変更を確認できます) )または、新しいコレクションを作成するdeepコピーを実行できます。
(ここで、String
のような不変の型がしばしば役に立ちます。それらを複製する必要はありません。参照をコピーするだけで、オブジェクトのコンテンツが変更されないことがわかります。)
あなたは私がそれをやって、私のためにうまく働く反射を使うことができます:
public Child(Parent parent){
for (Method getMethod : parent.getClass().getMethods()) {
if (getMethod.getName().startsWith("get")) {
try {
Method setMethod = this.getClass().getMethod(getMethod.getName().replace("get", "set"), getMethod.getReturnType());
setMethod.invoke(this, getMethod.invoke(parent, (Object[]) null));
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
//not found set
}
}
}
}
独自のコピーコンストラクタを作成する必要がありますか?これは、Carが新しいフィールドを取得するたびに更新する必要があります...
本質的には、はい-Javaでオブジェクトを変換することはできません。
幸い、すべてのコードを自分で書く必要はありません- commons-beanutils 、特に cloneBean のようなメソッドを調べてください。これには、新しいフィールドを取得するたびに更新する必要がないという利点もあります。
リフレクションAPIを使用して、各Carフィールドをループし、対応するTruckフィールドに値を割り当てることができます。これはトラック内で行うことができます。さらに、それはCarのプライベートフィールドにアクセスする唯一の方法です-少なくとも自動的に、セキュリティマネージャーが配置されておらず、プライベートフィールドへのアクセスを制限している場合。
上記のソリューションには、注意する必要がある制限があります。あるクラスから別のクラスにフィールドをコピーするためのアルゴリズムの簡単な要約を以下に示します。
ショーンパトリックフロイドのanalyze
関数を欠落しているステートメントで再現します。
private static Map<String, Field> analyze(Object object) {
if (object == null) throw new NullPointerException();
Map<String, Field> map = new TreeMap<String, Field>();
Class<?> current = object.getClass();
while (current != Object.class) {
Field[] declaredFields = current.getDeclaredFields();
for (Field field : declaredFields) {
if (!Modifier.isStatic(field.getModifiers())) {
if (!map.containsKey(field.getName())) {
map.put(field.getName(), field);
}
}
}
current = current.getSuperclass(); /* The missing statement */
}
return map;
}
Dozer などのマッピングフレームワークをいつでも使用できます。デフォルトでは( 詳細設定 なし)、getterメソッドとsetterメソッドを使用して、同じ名前のすべてのフィールドを1つのオブジェクトから別のオブジェクトにマッピングします。
依存:
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
</dependency>
コード:
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;
// ...
Car c = new Car();
/* ... c gets populated */
Truck t = new Truck();
Mapper mapper = new DozerBeanMapper();
mapper.map(c, t);
/* would like t to have all of c's values */
コピーコンストラクターが必要になりますが、コピーコンストラクターはリフレクションを使用して、2つのオブジェクト間の共通フィールドを検索し、「プロトタイプ」オブジェクトからそれらの値を取得して、子オブジェクトに設定できます。