从超类实例创建实例

时间:2011-08-03 10:21:19

标签: java

考虑以下情况:

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())。您可能会理解,手动复制每个成员是一个糟糕的解决方案,因为它会增加代码的复杂性和冗余。无论如何,谢谢你的回复!

10 个答案:

答案 0 :(得分:14)

没有一个简单的解决方案,因为没有一个通用的解决方案。基本上你没有B中的所有信息,所以你不能保证你会有一个“明智的”B对象。

可能只想在B中创建一个构造函数,该构造函数需要A并将所有A数据复制到新的B

答案 1 :(得分:4)

是一个(相对)微不足道的解决方案!

class B中实施一个构造函数,该构造函数接受class A的实例并复制字段。

语言本身没有泛型解决方案的原因之一是由于深度复制的问题。

例如,如果源对象包含更多Objects,而不是普通类型,那么通用复制操作符会做什么?只需复制参考(给出浅拷贝),或制作真正的副本?

如果其中一个对象是Collection,该怎么办?它是否也应该复制集合中的每个元素?

唯一合乎逻辑的结论是执行副本,但是你根本没有真正的副本。

答案 2 :(得分:4)

如果你不害怕commons-beanutils,你可以使用PropertyUtils

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

答案 3 :(得分:1)

如果您不需要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();
  }
}

答案 4 :(得分:0)

我也很震惊。 :)

你真的不能这样做:objB = objA;。 因为雷诺和宝马都是汽车,但并非所有汽车都是宝马。

感谢A as Car,B感谢宝马。

现在你说:

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

答案 5 :(得分:0)

...不是因为这是人们应该做的事情,而是因为我觉得这是一个挑战,这里有一些测试代码可以做一个简单的对象副本(使用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;
        }
    }
}

答案 6 :(得分:0)

也许你可以这样做:

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

您仍然会列出每个字段,但每个字段只列出一次。

答案 7 :(得分:0)

如果您更改方法以创建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。

如果您的班级AB共享相同的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;

答案 8 :(得分:0)

假设你的A类有一个非常整洁干净的setter和getter方法命名约定 setXXX(Object xxx)和相应的getXXX()返回相同的内容(对象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;
    }

答案 9 :(得分:-3)

我认为最好的方法是使用工厂方法从A对象创建B对象。

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