我尝试使用一个简单的示例来更好地进行深入分析:我有一个类Tool
和子类,它们正在扩展类Tool
:Hammer
,Saw
。两者都定义了一些字段,如weight
,两者都是重写方法getCost
,并且具有自己的实现。
Tool first_tool = new Hammer();
Tool second_tool = new Saw();
我需要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
结论:我想为所有子类提供一些简单的复制构造函数。
答案 0 :(得分: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);
}
}
}
这是你的子类,没有什么特别之处:
public class Saw extends Tool {
private int weight;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
一个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() );
}
}
答案 1 :(得分:3)
我会将Tool
抽象化,并将abstract copy
方法添加到Tool
。然后,每个子类都被迫提供自己的实现。这是一种相当OO的方法,并利用动态调度。
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...
}
否则,您将在Tool
中提供一个具体的实现,每次都需要更新,以便处理Tool
的新子类。此方法必须使用instanceof
,getClass()
或类似的非OO技术来创建正确的类。 EW。
记住Effective Java Item 10 tells us: Override clone
judiciously.
我应该提一下,复制只是通过复制权重属性来实现的。
假设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;
}
// ...
}
但是,我不建议这样做。这对我来说只是臭和丑。
答案 2 :(得分:1)
如果您不想要所有子类的特定副本(足够公平),则必须使用反射。如果您可以使用反射,则可能需要使用BeanUtils。
我会有一个实用程序类,比如说CopyUtil和一个复制方法来复制一个对象(使用BeanUtils)。出于某种原因,如果您需要将复制方法作为Tool的实例方法,只需编写一个并从那里调用此实用程序方法。
答案 3 :(得分:1)
我想建立Matt Ball答案的第一部分,并提到自Java 5以来,支持返回类型协方差。这意味着子类可以通过返回更具体的类型来覆盖方法。这将允许您执行以下操作:
abstract class Tool
{
public abstract Tool copy();
}
class Hammer
{
@Override
public Hammer copy()
{
Hammer h = new Hammer();
// copy fields from this to h
return h;
}
}
class Saw
{
@Override
public Saw copy()
{
Saw s = new Saw();
// copy fields from this to s
return s;
}
}
因此,当您使用各种Tool
个对象时,您知道可以调用copy()
并获取Tool
。当您只处理Hammer
个对象时,您知道copy()
将返回Hammer
(不需要强制转换)。