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