[英]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
.如果您的类A
和B
共享同一个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.