新手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获得一个新领域时都必须更新......
答案 0 :(得分:32)
是的,只需向Truck添加一个构造函数即可。您可能还想为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。
答案 1 :(得分:6)
如果您在项目中使用Spring,则可以使用ReflectionUtils。
答案 2 :(得分:4)
是的,您必须手动执行此操作。你还需要决定如何“深入”地复制东西。例如,假设汽车有一组轮胎 - 你可以做一个浅集合的副本(这样如果原始对象改变了它的集合的内容,新对象也会看到变化或者你可以做一个创建新集合的深副本。
(这是像String
这样的不可变类型经常派上用场的地方 - 没有必要克隆它们;你可以只复制引用并知道对象的内容不会改变。)
答案 3 :(得分:4)
我是否必须编写自己的复制构造函数?这必须是 每当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;
}
}
}
}
答案 4 :(得分:2)
我是否必须编写自己的复制构造函数?每次Car获得一个新领域时都必须更新......
基本上,是的 - 你不能只用Java转换对象。
幸运的是,您不必自己编写所有代码 - 请查看commons-beanutils,特别是cloneBean等方法。这样做的另一个好处是,每次获得新字段时都不必更新它!
答案 5 :(得分:2)
你可以使用反射我这样做,并为我工作正常:
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
}
}
}
}
答案 6 :(得分:1)
您可以使用反射API循环遍历每个Car字段,并将值分配给等效的Truck字段。这可以在卡车内完成。此外,它是访问Car私有字段的唯一方法 - 至少在自动意义上,只要安全管理器不到位并限制访问私有字段。
答案 7 :(得分:0)
您将需要一个复制构造函数,但您的复制构造函数可以使用反射来查找两个对象之间的公共字段,从“prototype”对象获取它们的值,并将它们设置在子对象上。
答案 8 :(得分:0)
您始终可以使用映射框架,例如Dozer。默认情况下(不带further configuration),它使用getter和setter方法将同名的所有字段从一个对象映射到另一个对象。
相关性:
<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 */
答案 9 :(得分:0)
上述解决方案存在您应该注意的限制。以下是将字段从一个类复制到另一个类的算法的简短摘要。
我用缺少的陈述复制了肖恩·帕特里克·弗洛伊德的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;
}
答案 10 :(得分:0)
我知道这是一个老问题,但是我讨厌在情况有所改善时遗漏过时的答案。
使用JSON更容易。将其转换为JSON并再次以子级形式返回。
这是一个Android Kotlin示例。
val gson = Gson()
val childClass = gson.fromJson(
gson.toJson(parentObject),
object: TypeToken<ChildObject>(){}.type
) as ChildObject
我认为在Java中基本上是这样。
Gson gson = new Gson()
ChildObject child = (ChildObject) gson.fromJson(
gson.toJson(parentObject),
TypeToken<ChildObject>(){}.getType()
)
您已经完成,没有混乱,只需简单的json输入,json输出即可。 如果您没有gson,我确定您还有其他json选项可用。
与进行反射和所有疯狂操作相比,这是一个整洁,快捷的工作。