Java:如何复制一个对象,使它来自同一个子类?

pet*_*tos 6 java oop reflection

我尝试使用一个简单的例子来更好地解析:我有一个类Tool和子类,它们正在扩展类Tool:Hammer, Saw. 两者都定义了一些字段,weight并且两者都是getCost自己实现的重写方法.

    Tool first_tool = new Hammer();
    Tool second_tool = new Saw();
Run Code Online (Sandbox Code Playgroud)

我需要一个Tool类中的方法,它将执行任何工具的副本,这种方式first_tool_copy来自同一个子类first_tool.我怎样才能做到这一点?我需要这样的东西:

    /* Copy tool, change parameters of copy, the original won't change */
    /* first_tool_copy will be instance of Hammer class */
    first_tool_copy = first_tool.copy
    first_tool_copy.weight = 100
Run Code Online (Sandbox Code Playgroud)

结论:我想为所有子类提供一些简单的复制构造函数.

Mau*_*res 5

对于这种情况可能有很多解决方案,但我相信最简单的解决方案是使用反射创建克隆对象并将字段从原始字段复制到副本.此代码的唯一要求是您的子类必须具有默认构造函数,但这看起来并不像真正的问题.

这是它的样子:

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Tool implements Cloneable {

private String name;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Override
public Object clone() {

    try {

        Tool instance = this.getClass().newInstance();

        List<Field> fields = new ArrayList<Field>();

        Class<?> kind = this.getClass();

        while ( kind != null ) {
            fields.addAll( Arrays.asList( kind.getDeclaredFields() ) );
            kind = kind.getSuperclass();
        }

        for ( Field field : fields ) {
            field.setAccessible(true);

            int mod = field.getModifiers();

            if ( !Modifier.isStatic( mod ) && !Modifier.isFinal( mod ) && !Modifier.isNative(mod) ) {
                Object value = field.get( this );
                field.set(instance, value);
            }

        }

        return instance;

    } catch (Exception e) {
        throw new UnsupportedOperationException(e);
    }

}

}
Run Code Online (Sandbox Code Playgroud)

这是你的子类,没有什么特别之处:

public class Saw extends Tool {

private int weight;

public int getWeight() {
    return weight;
}

public void setWeight(int weight) {
    this.weight = weight;
}

}
Run Code Online (Sandbox Code Playgroud)

以及一个JUnit测试用例,展示它是如何工作的:

public class SawTest {

@Test
public void testClone() {

    Saw original = new Saw();
    original.setName("Some saw");
    original.setWeight( 10 );

    Saw clone = (Saw) original.clone();

    Assert.assertTrue( original != clone );
    Assert.assertTrue( original.getClass().equals( clone.getClass() ) );
    Assert.assertEquals( original.getName(), clone.getName() );
    Assert.assertEquals( original.getWeight(), clone.getWeight() );

}

}
Run Code Online (Sandbox Code Playgroud)


Mat*_*all 3

我会进行抽象Tool,并向. 然后每个子类被迫提供自己的实现。这是一种相当面向对象的方法,并且利用了动态调度。abstract copyTool

abstract class Tool
{
    // snip...
    public abstract Tool copy();
    // snip...
}

class Hammer
{
    // snip...
    public Tool copy()
    {
        Hammer h = new Hammer();
        // copy fields from this to h
        return h;
    }
    // snip...
}
Run Code Online (Sandbox Code Playgroud)

否则,您将提供一个具体的实现,在Tool该实现中您每次都必须更新才能处理Tool. 此方法必须使用instanceofgetClass()或类似的非 OO 技术来创建正确的类。呃。


请记住《Effective Java Item 10》告诉我们的内容:明智地重写clone


我应该提到的是,该副本只是通过复制权重属性来制作的。

假设Tool实现类似于下面的类,您可以通过反射来完成此操作:

class Tool
{
    // ...

    public Tool () {}

    public int getWeight ()
    {
        // don't care about implementation
    }

    public void setWeight()
    {
        // don't care about implementation
    }

    // ignores all exceptions - not production code!
    public Tool copy() throws Exception
    {
        Tool copy = this.getClass().getConstructor().newInstance();
        copy.setWeight(this.getWeight());
        return copy;
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

不过我不会推荐这个。对我来说,它又臭又丑。