Java: создание объекта подкласса из родительского объекта - PullRequest
36 голосов
/ 12 марта 2009

Вопрос новичка на 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's values */

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

Ответы [ 10 ]

32 голосов
/ 12 марта 2009

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

7 голосов
/ 19 января 2012

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

4 голосов
/ 18 июня 2015

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

Совсем нет!

Попробуйте так:

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 голосов
/ 12 марта 2009

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

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

2 голосов
/ 25 марта 2015

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

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
            }
        }
    }
 }
2 голосов
/ 12 марта 2009

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

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

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

1 голос
/ 12 марта 2009

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

0 голосов
/ 05 июля 2016

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

  • Tom Hawtin : Используйте это, если ваш суперкласс имеет конструктор копирования. Если этого не произойдет, вам понадобится другое решение.
  • Christian : Используйте это, если суперкласс не расширяет какой-либо другой класс. Этот метод не копирует поля рекурсивно вверх.
  • Шон Патрик Флойд : Это общее решение для рекурсивного копирования всех полей вверх. Обязательно прочитайте комментарий @ jett о том, что для предотвращения бесконечного цикла необходимо добавить одну строку.

Я воспроизвожу функцию 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;
}
0 голосов
/ 11 марта 2016

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

Зависимость:

<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 */
0 голосов
/ 12 марта 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...