Создать экземпляр из экземпляра суперкласса
Рассмотрим следующий случай:
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()). Вы можете понять, что копирование каждого пользователя вручную - это плохое решение, поскольку оно добавляет сложности и избыточность кода. Спасибо за ваши ответы в любом случае!
Ответы
Ответ 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;
}
}