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

Создать экземпляр из экземпляра суперкласса

Рассмотрим следующий случай:

class A {
  int x;
  int y;
}

class B extends A {
  int z;
}

Теперь, где-то в коде, эти классы используются следующим образом:

A objA = getAFromSomewhere();
B objB = null;

И в определенной ситуации я хочу сделать что-то вроде

objB = objA; // can't do this
objB.z = someZ;

Конечно, реальные объекты немного сложнее, поэтому это не просто копирование двух целых чисел. Но они также не слишком сложны.

Я знаю, что я могу написать конструктор для B следующим образом:

public B(A anA) {
  this.a = anA.a;
  this.b = anA.b;

  this.z = 0;
}

Но если это действительно единственный способ, я предпочитаю слияние дополнительных членов B в A.

обновление с учетом ответов

Мой вопрос был недостаточно ясным. Я понимаю, что objB = objA; не может работать (таким образом, я попросил "что-то вроде", что означает что-то с сопоставимой сложностью кода), и я знаю о проблемах с неглубокими или глубокими копиями.
То, что я искал, - это возможность скопировать элементы базового класса (скажем, с помощью clone()). Вы можете понять, что копирование каждого пользователя вручную - это плохое решение, поскольку оно добавляет сложности и избыточность кода. Спасибо за ваши ответы в любом случае!

4b9b3361

Ответ 1

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

Вероятно, вы просто хотите создать конструктор в B, который берет A и копирует все данные A в новый B.

Ответ 2

Существует (относительно) тривиальное решение!

Внедрить конструктор в class B, который принимает экземпляр class A и копирует поля.

Одна из причин отсутствия универсального решения в языке - проблема глубокого копирования.

Например, если исходный объект содержит далее Objects, а не простые типы, что бы сделал общий оператор копирования? Просто скопируйте ссылку (дайте мелкую копию) или сделайте реальные копии?

Что тогда, если один из этих объектов является Collection? Должен ли он также копировать каждый элемент коллекции?

Единственный логический вывод - выполнить мелкую копию, но тогда вы действительно не получили копию вообще.

Ответ 3

Если вы не боитесь общедоступных вещей, вы можете использовать PropertyUtils

import org.apache.commons.beanutils.PropertyUtils;
class B extends A {
B(final A a) {
try {
        PropertyUtils.copyProperties(this, a);
    }
    catch (Exception e) {
    }
}
}

Ответ 4

Если вам не нужны полные функциональные возможности A, существует также возможность создать класс B, содержащий внутреннюю копию экземпляра A и реализацию некоторого минимального подмножества методов через интерфейс C, путем проксирования их в экземпляр.

class A implements IC {
  int x;
  int y;

  public C() {
  ...
  }
}

class B implements IC {
  private A _a;

  public B(A a) {
    _a = a;
  }

  public C() {
  _a.C();
  }
}

Ответ 5

Я тоже в шоке.:)

Вы действительно не можете этого сделать: objB = objA;. Потому что Renault и BMW - это автомобили, но не все автомобили - это BMW.

Спасибо, что как автомобиль, B как BMW.

Теперь вы говорите:

Car car = new Renault();
BMV bmv = car;  // you cannot do this. This is exactly your case. 

Ответ 6

... не потому, что это то, что люди должны делать, но больше, потому что я чувствовал себя как вызов, вот несколько тестовых кодов, которые делают простую копию объектов (используя методы setter и getter):

import java.lang.reflect.Method;
import org.junit.Test;

public class ObjectUtils {
    @Test
    public void test() {
        A a = new A();
        B b = new B();
        a.setX(1);
        a.setY(2);
        this.copyProperties(a, b);
    }
    private void copyProperties(Object obja, Object objb) {
        Method m[] = obja.getClass().getDeclaredMethods();
        for(int i=0;i<m.length;i++) {
            try {
                String name = m[i].getName();
                if(name.startsWith("get") || name.startsWith("is")) {
                    Class rtype = m[i].getReturnType();
                    String setter = name.replaceFirst("^(get|is)","set");
                    Class s = objb.getClass();
                    Method method = s.getMethod(setter,rtype);
                    Object[] args = new Object[1];
                    args[0] = m[i].invoke(obja);
                    method.invoke(objb,args[0]);
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }
    class A {
        int x;
        int y;
        /**
         * @return the x
         */
        public int getX() {
            return x;
        }
        /**
         * @param x the x to set
         */
        public void setX(int x) {
            this.x = x;
        }
        /**
         * @return the y
         */
        public int getY() {
            return y;
        }
        /**
         * @param y the y to set
         */
        public void setY(int y) {
            this.y = y;
        }       
    }
    class B extends A {
        int z;
        /**
         * @return the z
         */
        public int getZ() {
            return z;
        }
        /**
         * @param z the z to set
         */
        public void setZ(int z) {
            this.z = z;
        }
    }
}

Ответ 7

Возможно, вы могли бы сделать это:

class A {
    int x;
    int y;

    A(A a) {
        this.x = a.x;
        this.y = a.y;
    }
}

class B extends A {
    int z;

    B(A a) {
        super(a);
        z = 0;
    }
}

Вы по-прежнему публикуете все поля, но только один раз для каждого класса.

Ответ 8

Если вы измените свой метод на создание объектов B, вы можете просто сделать то, что хотите:

objB = (B) objA;
objB.z = someZ;

Это может быть даже встроено, но вам нужны скобки:

((B) objA).z = someZ;

Если нет, вам нужно пройти длинный путь с помощью конструкторов:

objB = new B(objA);
objB.z = someZ;

В этом случае я бы рекомендовал скопировать поля суперкласса в суперкласс. Иначе, если вы добавите поле в этот класс позже, вы можете забыть легко изменить копирование.

class A {
    int x;
    int y;
    public A(A objA) {
        x = objA.x;
        y = objA.y;
    }
}

class B extends A {
    int z;
    public B(A objA) {
        super(objA);
    }
}

Я предпочитаю слияние дополнительных членов B в A.

Вы можете сделать это, если ваши классы A и B используют один и тот же package или переменные в вашем классе A объявляются как protected. Затем вы можете просто получить доступ к полям суперкласса.

class A {
  protected int x;
  protected int y;
}

class B extends A {
  int z;

  void merge(A a){
    super.x = a.x; 
    y = a.y;        // you do not *need* to use the super keyword, but it is a good hint to
                    // yourself if you read your program later and might wonder ‘where is
                    // that y declared?’
  }
}

Использование, конечно же:

objB = new B();
objB.merge(objA);
objB.z = someZ;

Ответ 9

Предполагая, что ваш класс A имеет очень аккуратное и чистое соглашение об именовании метода установки и метода getter, например setXXX(Object xxx) и соответствующий getXXX(), который возвращает одно и то же (Object xxx) в качестве параметра, переданного в setXXX()

Я написал полезный метод, используя отражение

public static B createSubclassInstance(A a) throws SecurityException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        Method[] aMethods = Class.forName("package.A").getDeclaredMethods();
        B b = new B(); 
        for (Method aMethod : aMethods) {
            String aMethodName = aMethod.getName();
            Class param = aMethod.getReturnType();
            if (methodName.startsWith("get")){
                String setterMethodName = methodName.replaceFirst("get", "set");
                Method bMethod = Class.forName("package.B").getMethod(setterMethodName);
                Object retValA = aMethod.invoke(a,null);
                bMethod.invoke(b,retValA);
            }

        }
        return b;
    }

Ответ 10

Я думаю, лучший способ - использовать метод factory для создания объектов B из объектов A.

class BFactory
{
    public static B createB(A a)
    {
     B b = new B();
     copy(a,b);

     return b;
    }

    private static <X,Y> void copy(X src,Y dest) throws Exception
    {
        List<Field> aFields = getAllFields(src.getClass());
        List<Field> bFields = getAllFields(dest.getClass());

        for (Field aField : aFields) {
            aField.setAccessible(true);
            for (Field bField : bFields) {
                bField.setAccessible(true);
                if (aField.getName().equals(bField.getName()))
                {
                    bField.set(dest, aField.get(src));
                }
            }
        }
    }

    private static List<Field> getAllFields(Class type)
    {
        ArrayList<Field> allFields = new ArrayList<Field>();
        while (type != Object.class)
        {
            Collections.addAll(allFields, type.getDeclaredFields());
            type = type.getSuperclass();
        }
        return allFields;
    }
}