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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/638171/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:16:19  来源:igfitidea点击:

Java: Creating a subclass object from a parent object

javainheritanceconstructor

提问by

Newbie Java question. Say I have:

新手Java问题。说我有:

public class Car{
  ...
}

public class Truck extends Car{
  ...
}

Suppose I already have a Car object, how do I create a new Truck object from this Car object, so that all the values of the Car object is copied into my new Truck object? Ideally I could do something like this:

假设我已经有一个 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 */

Would I have to write my own copy constructor? This would have to be updated everytime Car gets a new field...

我是否必须编写自己的复制构造函数?每次 Car 获得新字段时都必须更新...

回答by Tom Hawtin - tackline

Yes, just add a constructor to Truck. You will probably want to add a constructor to Car also, though not necessarily public:

是的,只需为 Truck 添加一个构造函数。您可能还想向 Car 添加一个构造函数,但不一定是公开的:

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

public class Truck extends Car {
    public Truck(Car orig) {
        super(orig);
    }
    ...
}

As a rule it's generally best to make classes either leaf (and you might want to mark those final) or abstract.

通常,最好将类设为叶类(您可能希望将它们标记为 final)或抽象类。

It looks as if you want a Carobject, and then have the same instance turn into a Truck. A better way of doing this is to delegate behaviour to another object within Car(Vehicle). So:

看起来好像您想要一个Car对象,然后将相同的实例变成Truck. 这样做的更好方法是将行为委托给Car( Vehicle) 中的另一个对象。所以:

public final class Vehicle {
    private VehicleBehaviour behaviour = VehicleBehaviour.CAR;

    public void becomeTruck() {
        this.behaviour =  VehicleBehaviour.TRUCK;
    } 
    ...
}

If you implement Cloneablethen you can "automatically" copy an object to a instance of the same class. However there are a number of problems with that, including having to copy each field of mutable objects which is error-prone and prohibits the use of final.

如果您实现,Cloneable那么您可以“自动”将对象复制到同一类的实例。然而,这存在许多问题,包括必须复制易出错且禁止使用 final 的可变对象的每个字段。

回答by Tom Hawtin - tackline

Would I have to write my own copy constructor? This would have to be updated everytime Car gets a new field...

我是否必须编写自己的复制构造函数?每次 Car 获得新字段时都必须更新...

Essentially, yes - you can't just convert an object in Java.

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

Fortunately you don't have to write all the code yourself - look into commons-beanutils, specifically methods like cloneBean. This has the added advantage that you don't have to update it every time it gets a new field!

幸运的是,您不必自己编写所有代码 - 查看commons-beanutils,特别是像cloneBean这样的方法。这有一个额外的好处,即您不必每次获得新字段时都更新它!

回答by tvanfosson

You will need a copy constructor, but your copy constructor can use reflection to find the common fields between the two objects, get their values from the "prototype" object, and set them on the child object.

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

回答by Jon Skeet

Yes, you have to do this manually. You'll also need to decide how "deeply" to copy things. For instance, suppose the Car has a collection of tyres - you could do a shallowcopy of the collection (such that if the original object changes the contents of its collection, the new object would see the change too) or you could do a deepcopy which created a new collection.

是的,您必须手动执行此操作。您还需要决定复制事物的“深度”。例如,假设 Car 有一个轮胎集合——你可以对这个集合做一个拷贝(这样如果原始对象改变了它的集合的内容,新对象也会看到变化)或者你可以做一个创建一个新集合的副本。

(This is where immutable types like Stringoften come in handy - there's no need to clone them; you can just copy the reference and know that the contents of the object won't change.)

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

回答by Martin OConnor

You could use the reflection API to loop through each of the Car fields and assign the value to the equivalent Truck fields. This can be done within truck. Further it is the only way to access the private fields of Car - at least in an automatic sense, providing that a security manager is not in place and restricting access to private field.

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

回答by Pawel.Duleba

If you are using Spring in your project you may use ReflectionUtils.

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

回答by rcorbellini

you can use reflection i do it and work fine for me:

你可以使用反射,我做到了,对我来说效果很好:

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

回答by Christian

Would I have to write my own copy constructor? This would have to be updated everytime Car gets a new field...

我是否必须编写自己的复制构造函数?每次 Car 获得新字段时都必须更新...

Not at all!

一点也不!

Try this way:

试试这个方法:

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

回答by schnatterer

You could always use a mapping Framework such as Dozer. By default (without further configuration), it maps all fields of the same name from one object to another using the getter and setter methods.

您始终可以使用诸如Dozer 之类的映射框架。默认情况下(无需进一步配置),它使用 getter 和 setter 方法将所有同名字段从一个对象映射到另一个对象。

Dependency:

依赖:

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

Code:

代码:

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 */

回答by Moshe Rubin

The solutions presented above have limitations you should be aware of. Here's a short summary of algorithms for copying fields from one class to another.

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

  • Tom Hawtin: Use this if your superclass has a copy constructor. If it does not you will need a different solution.
  • Christian: Use this if the superclass does not extend any other class. This method does not copy fields recursively upwards.
  • Sean Patrick Floyd: This is a generic solution for copying all fields recursively upwards. Be sure to read @jett's comment that a single line must be added to prevent an endless loop.
  • Tom Hawtin:如果您的超类有一个复制构造函数,请使用它。如果不是,您将需要不同的解决方案。
  • Christian:如果超类不扩展任何其他类,请使用它。此方法不会向上递归复制字段。
  • Sean Patrick Floyd:这是一个用于向上递归复制所有字段的通用解决方案。请务必阅读@jett 的评论,即必须添加一行以防止无限循环。

I reproduce Sean Patrick Floyd's analyzefunction with the missing statement:

analyze用缺失的语句重现了 Sean Patrick Floyd 的函数:

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