2011-08-03 104 views
13

创建实例考虑以下情况:从父实例

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; 

当然,真正的对象有点复杂,所以它不只是复制两个我NTS。但他们也不太复杂。

我知道我能写这样对于B构造:

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

    this.z = 0; 
} 

但如果这真的是唯一的方式,我更喜欢B的其他成员并入A.

更新考虑回答

我的问题还不够清楚。我明白objB = objA;无法工作(因此我要求“类似的东西”,这意味着具有类似代码复杂性的东西),并且我知道浅与深拷贝的问题。
我在找的是复制基类成员的可能性(比方说使用clone())。您可能会明白,手动复制每个成员是一个不好的解决方案,因为它会增加代码的复杂性和冗余性。无论如何感谢您的回复!

回答

12

对此没有简单的解决方案,因为没有万能解决方案。基本上,您不具有B内的所有信息,因此您无法保证您拥有“合理的”B对象。

可能只是想创建B一个构造函数,需要一个A和复制所有数据A到新B

+0

我已经修改我的问题以将此考虑在内。看起来真的没有优雅的解决方案 –

+1

@ didi_X8:确实 - 但重要的是你要了解*为什么没有一般的优雅解决方案。例如,如果我能够写出:“Object x =”Hello“,这意味着什么? FileOutputStream stream = x;'? –

+1

“您可能只是想在B中创建一个构造函数,它需要一个A并将所有A数据复制到新的B.”是的,那正是我想要的。但没有单独提及A的每个成员。 –

3

还有一个(相对)琐碎的解决方案!

class B中实施构造函数,该构造函数采用class A的实例并复制这些字段。

原因之一是没有通用解决方案在语言本身是因为深层复制的问题。

例如,如果源对象进一步包含Objects而不是普通类型,那么通用复制操作符会做什么?只需复制参考文献(给出浅拷贝),或者制作真实拷贝?

那么如果其中一个对象是Collection?它是否也应该复制馆藏的每一个元素?

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

0

我也很震惊。:)

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

感谢A作为Car,B作为宝马。

现在你说:

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

我明白我不能这样做,那不是问题所在。我只是想知道是否没有简单的方法来说(在构造函数中):从“source”obj(不管它是什么)复制所有共享(基类)部件,并单独初始化其他(宝马)部件。 –

-3

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

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

因为答案没有考虑到这个问题而被低估:这个问题基本上是说“我怎么能避免提到每个成员A和B都有共同点? –

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

你还列出每个领域,但每个班只有一次。

3

如果你不害怕的公地的BeanUtils可以使用PropertyUtils

import org.apache.commons.beanutils.PropertyUtils; 
class B extends A { 
B(final A a) { 
try { 
     PropertyUtils.copyProperties(this, a); 
    } 
    catch (Exception e) { 
    } 
} 
} 
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; 
+0

你不能将超类转换成子类 – Line

+0

”如果你改变您创建'B'对象的方法...“变量可能被声明为'A',但您仍然可以使用强制转换访问'B'的成员。 – Paramaeleon

1

如果您不需要的全部功能,还创建B类的选项,拿着实例的内部复制和执行一些通过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(); 
    } 
} 
0

假设你的类A有像 setXXX(Object xxx)非常整洁干净setter和getter方法的命名约定corrresponding getXXX()返回同样的事情(对象XXX)作为PARAM传递给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; 
    }