简体   繁体   English

从超类实例创建实例

[英]Create instance from superclass instance

Consider the following case:考虑以下情况:

class A {
  int x;
  int y;
}

class B extends A {
  int z;
}

Now, somewhere in the code this classes are used like this:现在,在代码的某个地方,这些类是这样使用的:

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

And in a certain situation I want to do something like在某种情况下,我想做一些类似的事情

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

Of course the real objects are a bit more complicated, so it's not just about copying two ints.当然,真实的对象要复杂一些,所以不仅仅是复制两个整数。 But they aren't overly complex either.但它们也不是太复杂。

I know I can write a constructor for B like this:我知道我可以像这样为 B 编写一个构造函数:

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

  this.z = 0;
}

But if that's really the only way, I prefer merging the additional members of B into A.但如果这真的是唯一的方法,我更喜欢将 B 的其他成员合并到 A 中。

update considering the answers更新考虑答案

My question was not clear enough.我的问题不够清楚。 I understand that objB = objA;我明白 objB = objA; can't work (thus I asked for "something like", meaning something with comparable code complexity) and I know about the issues with shallow vs deep copies.不能工作(因此我要求“类似的东西”,意思是具有可比代码复杂性的东西)并且我知道浅拷贝和深拷贝的问题。
What I was looking for is a possibility to copy the members of a base class (let's say using clone()).我正在寻找的是复制基类成员的可能性(假设使用 clone())。 You may understand that copying every member manually is a bad solution as it adds complexity and redundancy to the code.您可能理解手动复制每个成员是一个糟糕的解决方案,因为它增加了代码的复杂性和冗余。 Thanks for your replies anyway!无论如何感谢您的回复!

There's no trivial solution to this because there's no one-size-fits-all solution.对此没有简单的解决方案,因为没有一刀切的解决方案。 Basically you don't have all the information within a B , so you can't guarantee you would have a "sensible" B object.基本上你没有B所有信息,所以你不能保证你会有一个“合理的” B对象。

You probably just want to create a constructor in B which takes an A and copies all the A data into the new B .可能只想在B创建一个构造函数,它接受一个A并将所有A数据复制到新的B

If you're not scared of commons-beanutils you can use PropertyUtils如果你不害怕 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) {
    }
}
}

There is a (relatively) trivial solution!一个(相对)平凡解!

Implement a constructor in class B that takes an instance of class A and copies the fields.实现构造class B ,它利用一个实例class A和副本等领域。

One of the reasons there's no generic solution in the language itself is because of the problem of deep copying.语言本身没有通用解决方案的原因之一是深度复制的问题。

For example, if the source object contains further Objects , as opposed to plain types, what would the generic copy operator do?例如,如果源对象包含更多的Objects ,而不是普通类型,那么通用复制运算符会做什么? Just copy the reference (giving a shallow copy), or make real copies?只是复制参考(提供浅拷贝),还是制作真正的副本?

What then if one of those objects is a Collection ?那么如果这些对象之一是Collection呢? Should it also copy every element of the collection, too?它也应该复制集合的每个元素吗?

The only logical conclusion would be to perform a shallow copy, but then you haven't really got a copy at all.唯一合乎逻辑的结论是执行拷贝,但是您根本没有真正得到副本。

Perhaps you could do this:也许你可以这样做:

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

You're still listing every field, but only once per class.您仍在列出每个字段,但每个类只列出一次。

...not because this is what people should do but more because I felt like a challenge, here is some test code which does a simple copy of the objects (using setter and getter methods): ...不是因为这是人们应该做的,而是因为我觉得这是一个挑战,这里有一些测试代码,它对对象进行了简单的复制(使用 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;
        }
    }
}

If you do not need full functionality of A, there is also an option to create class B, holding internal copy of A instance and implementing some minimal subset of methods via C interface by proxying them to instance.如果您不需要 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();
  }
}

Assuming that your class A has a very neat and clean setter and getter method naming convention like setXXX(Object xxx) and corrresponding getXXX() which returns the same thing (Object xxx ) as a param passed to setXXX()假设你的类 A 有一个非常整洁干净的 setter 和 getter 方法命名约定,如setXXX(Object xxx)和相应的getXXX()返回相同的东西 (Object xxx ) 作为传递给setXXX()的参数

I have written a utility method using reflection我写了一个使用反射的实用方法

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

I am shocked too.我也很震惊。 :) :)

You really cannot do this: objB = objA;你真的不能这样做: objB = objA; . . Because Renault and BMW are cars but not all cars are BMW.因为雷诺和宝马都是汽车,但并非所有汽车都是宝马。

Thank about A as Car, B as BMW.感谢 A 为汽车,B 为宝马。

Now you say:现在你说:

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

If you change your method to create B objects, you can just do what you want using:如果你改变你的方法来创建B对象,你可以使用你想做的事情:

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

This can even be inlined, but you need parentheses:这甚至可以内联,但你需要括号:

((B) objA).z = someZ;

If not, you have to go the long way using constructors:如果没有,您必须使用构造函数走很长的路:

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

In this case I would recommend to copy the fields of the superclass in the superclass.在这种情况下,我建议在超类中复制超类的字段。 Else, if you add a field to that class later, you may forget to change the copying more easily.否则,如果您稍后向该类添加字段,您可能会更容易忘记更改副本。

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

I prefer merging the additional members of B into A.我更喜欢将 B 的其他成员合并到 A 中。

You can do this if your classes A and B share the same package or if the variables in your A class are declared as protected .如果您的类AB共享同一个package或者如果您的A类中的变量声明为protected可以执行此操作。 Then you can just access the fields of the superclass.然后你就可以访问超类的字段。

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?’
  }
}

Useage, of course, is:用途当然是:

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

I think best way is to use a factory method to create B objects from A objects.我认为最好的方法是使用工厂方法从 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