Java:从父对象创建子类对象

Java:从父对象创建子类对象,java,inheritance,constructor,Java,Inheritance,Constructor,新手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

新手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获得一个新字段时,都必须更新此字段…

是的,只需向Truck添加一个构造函数即可。您可能还希望向Car添加构造函数,但不一定是公共的:

public class Car {
    protected Car(Car orig) {
    ...
}

public class Truck extends Car {
    public Truck(Car orig) {
        super(orig);
    }
    ...
}
一般来说,最好将类设置为叶子类(您可能希望将它们标记为final)或抽象类

看起来好像您想要一个
汽车
对象,然后将同一个实例变成
卡车
。一种更好的方法是将行为委托给
汽车
汽车
)中的另一个对象。因此:

如果实现了
Cloneable
,则可以“自动”将对象复制到同一类的实例中。然而,这也有很多问题,包括必须复制易变对象的每个字段,这很容易出错,并且禁止使用final

我是否需要编写自己的副本构造函数?这将不得不更新每次汽车得到一个新的领域

本质上,是的-您不能仅仅在Java中转换对象


幸运的是,您不必自己编写所有的代码——仔细研究,特别是像这样的方法。这有一个额外的优势,你不必每次更新它得到一个新的领域

您需要一个复制构造函数,但是您的复制构造函数可以使用反射来查找两个对象之间的公共字段,从“prototype”对象获取它们的值,并在子对象上设置它们。

是的,您必须手动执行此操作。您还需要决定复制内容的“深度”。例如,假设汽车有一个轮胎集合-您可以对集合进行浅拷贝(这样,如果原始对象更改其集合的内容,新对象也会看到更改),或者您可以进行创建新集合的深拷贝


(这就是像
String
这样的不可变类型经常派上用场的地方——不需要克隆它们;您只需复制引用,就知道对象的内容不会改变。)

您可以使用反射API循环每个Car字段,并将值分配给等效的Truck字段。这可以在卡车内完成。此外,这是访问Car私有字段的唯一方法-至少在自动意义上是这样,前提是安全管理器不到位,并且限制对私有字段的访问。

如果您在项目中使用Spring,您可以使用ReflectionUtils。

您可以使用reflection i do it,对我来说工作正常:

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
            }
        }
    }
 }
我是否需要编写自己的副本构造函数?这必须是 每一次更新汽车得到一个新的领域

一点也不

试着这样做:

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;
                }
            }
        }
    }

您可以始终使用映射框架,例如。默认情况下(没有),它使用getter和setter方法将相同名称的所有字段从一个对象映射到另一个对象

依赖关系:

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.5.1</version>
</dependency>

上述解决方案有一些局限性,您应该注意。下面是将字段从一个类复制到另一个类的算法的简短摘要

  • :如果您的超类具有复制构造函数,请使用此选项。如果没有,您将需要一个不同的解决方案
  • :如果超类不扩展任何其他类,则使用此选项。此方法不会向上递归复制字段
  • :这是向上递归复制所有字段的通用解决方案。请务必阅读@jett的评论,即必须添加一行以防止无休止的循环
我用缺少的语句重现Sean Patrick Floyd的
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;
}
私有静态映射分析(对象){
如果(object==null)抛出新的NullPointerException();
Map Map=newtreemap();
Class current=object.getClass();
while(当前!=Object.class){
Field[]declaredFields=current.getDeclaredFields();
for(字段:declaredFields){
如果(!Modifier.isStatic(field.getModifiers())){
如果(!map.containsKey(field.getName())){
map.put(field.getName(),field);
}
}
}
current=current.getSuperclass();/*缺少的语句*/
}
返回图;
}

我知道这是一个老问题,但当情况有所改善时,我不愿意留下过时的答案

使用JSON要容易得多。将其转换为JSON并作为子级返回

下面是一个Android Kotlin示例

    val gson = Gson()    
    val childClass = gson.fromJson(
        gson.toJson(parentObject), 
        object: TypeToken<ChildObject>(){}.type
    ) as ChildObject
val gson=gson()
val childClass=gson.fromJson(
toJson(parentObject),
对象:TypeToken(){}.type
)作为儿童对象
我认为在Java中基本上是这样的

Gson gson = new Gson()
ChildObject child = (ChildObject) gson.fromJson(
    gson.toJson(parentObject),
    TypeToken<ChildObject>(){}.getType()
) 
Gson-Gson=new-Gson()
ChildObject child=(ChildObject)gson.fromJson(
toJson(parentObject),
TypeToken(){}.getType()
) 
完成了,没有混乱,只是简单的json输入,json输出。 如果您没有gson,我相信您可以使用其他json选项


它比反射和所有那些疯狂的行为更干净、更快。

是的,你可以用反射或者一些预先存在的框架来做这类事情。不过,要注意反思。在某些情况下它还可以,但是它仍然存在一些性能问题,所以请不要将它用于大容量的内容。+1从最初的测试来看,这非常有效。既然spring已经为您做了,为什么还要做这些事情呢?例如,ReflectionUtils.shallowCopyFieldState(baseObj,extendedObj)可以工作。因为OP想要复制所有字段,所以应该对您的答案做两个更改:使用getDeclaredFields而不是getFields,并访问父类的字段,n
    val gson = Gson()    
    val childClass = gson.fromJson(
        gson.toJson(parentObject), 
        object: TypeToken<ChildObject>(){}.type
    ) as ChildObject
Gson gson = new Gson()
ChildObject child = (ChildObject) gson.fromJson(
    gson.toJson(parentObject),
    TypeToken<ChildObject>(){}.getType()
)