考虑以下情况:
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())。您可能会理解,手动复制每个成员是一个糟糕的解决方案,因为它会增加代码的复杂性和冗余。无论如何,谢谢你的回复!
答案 0 :(得分:14)
没有一个简单的解决方案,因为没有一个通用的解决方案。基本上你没有B
中的所有信息,所以你不能保证你会有一个“明智的”B
对象。
您可能只想在B
中创建一个构造函数,该构造函数需要A
并将所有A
数据复制到新的B
中
答案 1 :(得分:4)
是一个(相对)微不足道的解决方案!
在class B
中实施一个构造函数,该构造函数接受class A
的实例并复制字段。
语言本身没有泛型解决方案的原因之一是由于深度复制的问题。
例如,如果源对象包含更多Objects
,而不是普通类型,那么通用复制操作符会做什么?只需复制参考(给出浅拷贝),或制作真正的副本?
如果其中一个对象是Collection
,该怎么办?它是否也应该复制集合中的每个元素?
唯一合乎逻辑的结论是执行浅副本,但是你根本没有真正的副本。
答案 2 :(得分:4)
如果你不害怕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) {
}
}
}
答案 3 :(得分:1)
如果您不需要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();
}
}
答案 4 :(得分:0)
我也很震惊。 :)
你真的不能这样做:objB = objA;
。
因为雷诺和宝马都是汽车,但并非所有汽车都是宝马。
感谢A as Car,B感谢宝马。
现在你说:
Car car = new Renault();
BMV bmv = car; // you cannot do this. This is exactly your case.
答案 5 :(得分: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;
}
}
}
答案 6 :(得分: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;
}
}
您仍然会列出每个字段,但每个字段只列出一次。
答案 7 :(得分: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。
如果您的班级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;
答案 8 :(得分:0)
假设你的A类有一个非常整洁干净的setter和getter方法命名约定
setXXX(Object xxx)
和相应的getXXX()
返回相同的内容(对象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;
}
答案 9 :(得分:-3)
我认为最好的方法是使用工厂方法从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;
}
}