繁体   English   中英

从超类实例创建实例

[英]Create instance from superclass instance

考虑以下情况:

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

对此没有简单的解决方案,因为没有一刀切的解决方案。 基本上你没有B所有信息,所以你不能保证你会有一个“合理的” B对象。

可能只想在B创建一个构造函数,它接受一个A并将所有A数据复制到新的B

如果你不害怕 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) {
    }
}
}

一个(相对)平凡解!

实现构造class B ,它利用一个实例class A和副本等领域。

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

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

那么如果这些对象之一是Collection呢? 它也应该复制集合的每个元素吗?

唯一合乎逻辑的结论是执行拷贝,但是您根本没有真正得到副本。

也许你可以这样做:

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

您仍在列出每个字段,但每个类只列出一次。

...不是因为这是人们应该做的,而是因为我觉得这是一个挑战,这里有一些测试代码,它对对象进行了简单的复制(使用 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;
        }
    }
}

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

假设你的类 A 有一个非常整洁干净的 setter 和 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;
    }

我也很震惊。 :)

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

感谢 A 为汽车,B 为宝马。

现在你说:

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

如果你改变你的方法来创建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;

我认为最好的方法是使用工厂方法从 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;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM