Подтвердить что ты не робот

Java: создание объекта подкласса из родительского объекта

Новый вопрос Java. Скажем, у меня есть:

public class Car{
  ...
}

public class Truck extends Car{
  ...
}

Предположим, у меня уже есть объект Car, как мне создать новый объект Truck из этого объекта Car, чтобы все значения объекта Car были скопированы в мой новый объект Truck? В идеале я мог бы сделать что-то вроде этого:

Car c = new Car();
/* ... c gets populated */

Truck t = new Truck(c);
/* would like t to have all of c values */

Должен ли я написать собственный конструктор копирования? Это нужно обновлять каждый раз, когда автомобиль получает новое поле...

4b9b3361

Ответ 1

Да, просто добавьте конструктор в 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.

Ответ 2

Если вы используете Spring в своем проекте, вы можете использовать ReflectionUtils.

Ответ 3

Да, вам нужно сделать это вручную. Вам также нужно будет решить, как "глубоко" копировать вещи. Например, предположим, что у автомобиля есть коллекция шин - вы можете сделать небольшую копию коллекции (например, если исходный объект изменит содержимое своей коллекции, новый объект тоже увидит это изменение), или вы можете сделать глубокий который создал новую коллекцию.

(Здесь часто могут использоваться неизменные типы типа String - нет необходимости клонировать их, вы можете просто скопировать ссылку и знать, что содержимое объекта не изменится.)

Ответ 4

Должен ли я написать собственный конструктор копирования? Это должно быть обновляется каждый раз. Автомобиль получает новое поле...

Совсем нет!

Попробуйте следующим образом:

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

Ответ 5

Должен ли я написать собственный конструктор копирования? Это нужно обновлять каждый раз, когда автомобиль получает новое поле...

По существу, да - вы не можете просто преобразовать объект в Java.

К счастью, вам не нужно писать весь код самостоятельно - посмотрите commons-beanutils, в частности такие методы, как cloneBean. Это имеет дополнительное преимущество, что вам не нужно обновлять его каждый раз, когда он получает новое поле!

Ответ 6

вы можете использовать отражение, я делаю это и отлично работаю для меня:

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

Ответ 7

Вы можете использовать API отражения для прокрутки каждого из полей Car и присвоить значение эквивалентным полям Truck. Это можно сделать в грузовике. Кроме того, это единственный способ получить доступ к частным полям Car - по крайней мере, в автоматическом смысле, при условии, что диспетчер безопасности не работает и ограничивает доступ к частному полю.

Ответ 8

Вам понадобится конструктор копирования, но ваш конструктор копирования может использовать отражение, чтобы найти общие поля между двумя объектами, получить их значения из объекта "prototype" и установить их на дочернем объекте.

Ответ 9

Вы всегда можете использовать инфраструктуру отображения, такую ​​как Dozer. По умолчанию (без дополнительная конфигурация), он отображает все поля одного и того же имени из одного объекта в другой с использованием методов 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 values */

Ответ 10

Представленные выше решения имеют ограничения, о которых вы должны знать. Вот краткое описание алгоритмов для копирования полей из одного класса в другой.

  • Tom Hawtin: используйте это, если ваш суперкласс имеет конструктор копирования. Если это не так, вам понадобится другое решение.
  • Кристиан: используйте это, если суперкласс не расширяет какой-либо другой класс. Этот метод не копирует поля рекурсивно вверх.
  • Шон Патрик Флойд: Это общее решение для копирования всех полей рекурсивно вверх. Обязательно прочтите комментарий @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;
}