副本构建器
克隆看起来要求举办很是巨大的配置,好像还该有另一种替代方案。一个步伐是建造非凡的构建器,令其认真复制一个工具。在C++中,这叫作“副本构建器”。刚开始的时候,这好象是一种很是显然的办理方案(假如你是C++措施员,这个要领就更显亲切)。下面是一个实际的例子:
//: CopyConstructor.java
// A constructor for copying an object
// of the same type, as an attempt to create
// a local copy.
class FruitQualities {
private int weight;
private int color;
private int firmness;
private int ripeness;
private int smell;
// etc.
FruitQualities() { // Default constructor
// do something meaningful...
}
// Other constructors:
// ...
// Copy constructor:
FruitQualities(FruitQualities f) {
weight = f.weight;
color = f.color;
firmness = f.firmness;
ripeness = f.ripeness;
smell = f.smell;
// etc.
}
}
class Seed {
// Members...
Seed() { /* Default constructor */ }
Seed(Seed s) { /* Copy constructor */ }
}
class Fruit {
private FruitQualities fq;
private int seeds;
private Seed[] s;
Fruit(FruitQualities q, int seedCount) {
fq = q;
seeds = seedCount;
s = new Seed[seeds];
for(int i = 0; i < seeds; i++)
s[i] = new Seed();
}
// Other constructors:
// ...
// Copy constructor:
Fruit(Fruit f) {
fq = new FruitQualities(f.fq);
seeds = f.seeds;
// Call all Seed copy-constructors:
for(int i = 0; i < seeds; i++)
s[i] = new Seed(f.s[i]);
// Other copy-construction activities...
}
// To allow derived constructors (or other
// methods) to put in different qualities:
protected void addQualities(FruitQualities q) {
fq = q;
}
protected FruitQualities getQualities() {
return fq;
}
}
class Tomato extends Fruit {
Tomato() {
super(new FruitQualities(), 100);
}
Tomato(Tomato t) { // Copy-constructor
super(t); // Upcast for base copy-constructor
// Other copy-construction activities...
}
}
class ZebraQualities extends FruitQualities {
private int stripedness;
ZebraQualities() { // Default constructor
// do something meaningful...
}
ZebraQualities(ZebraQualities z) {
super(z);
stripedness = z.stripedness;
}
}
class GreenZebra extends Tomato {
GreenZebra() {
addQualities(new ZebraQualities());
}
GreenZebra(GreenZebra g) {
super(g); // Calls Tomato(Tomato)
// Restore the right qualities:
addQualities(new ZebraQualities());
}
void evaluate() {
ZebraQualities zq =
(ZebraQualities)getQualities();
// Do something with the qualities
// ...
}
}
public class CopyConstructor {
public static void ripen(Tomato t) {
// Use the "copy constructor":
t = new Tomato(t);
System.out.println("In ripen, t is a " +
t.getClass().getName());
}
public static void slice(Fruit f) {
f = new Fruit(f); // Hmmm... will this work?
System.out.println("In slice, f is a " +
f.getClass().getName());
}
public static void main(String[] args) {
Tomato tomato = new Tomato();
ripen(tomato); // OK
slice(tomato); // OOPS!
GreenZebra g = new GreenZebra();
ripen(g); // OOPS!
slice(g); // OOPS!
g.evaluate();
}
} ///:~
这个例子第一眼看上去显得有点奇怪。差异水果的质量必定有所区别,但为什么只是把代表那些质量的数据成员直接置入Fruit(水果)类?有两方面大概的原因。第一个是我们大概想轻便地插入或修改质量。留意Fruit有一个protected(受到掩护的)addQualities()要领,它答允衍生类来举办这些插入或修改操纵(各人或者会认为最合乎逻辑的做法是在Fruit中利用一个protected构建器,用它获取FruitQualities参数,但构建器不能担任,所以不行在第二级或级数更深的类中利用它)。通过将水果的质量置入一个独立的类,可以获得更大的机动性,个中包罗可以在特定Fruit工具的存在期间半途变动质量。
之所以将FruitQualities设为一个独立的工具,另一个原因是思量到我们有时但愿添加新的质量,可能通过担任与多形性改变行为。留意对GreenZebra来说(这实际是西红柿的一类——我已栽种乐成,它们的确令人难以置信),构建器会挪用addQualities(),并为其通报一个ZebraQualities工具。该工具是从FruitQualities衍生出来的,所以能与基本类中的FruitQualities句柄接洽在一起。虽然,一旦GreenZebra利用FruitQualities,就必需将其下溯造型成为正确的范例(就象evaluate()中展示的那样),但它必定知道范例是ZebraQualities。
各人也看到有一个Seed(种子)类,Fruit(各人都知道,水果含有本身的种子)包括了一个Seed数组。
最后,留意每个类都有一个副本构建器,并且每个副本构建器都必需体贴为基本类和成员工具挪用副本构建器的问题,从而得到“深层复制”的结果。对副本构建器的测试是在CopyConstructor类内举办的。要领ripen()需要获取一个Tomato参数,并对其执行副本构建事情,以便复制工具:
t = new Tomato(t);
而slice()需要获取一个更通例的Fruit工具,并且对它举办复制:
f = new Fruit(f);
它们都在main()中陪伴差异种类的Fruit举办测试。下面是输出功效:
In ripen, t is a Tomato In slice, f is a Fruit In ripen, t is a Tomato In slice, f is a Fruit
#p#分页标题#e#
从中可以看出一个问题。在slice()内部对Tomato举办了副本构建事情今后,功效便不再是一个Tomato工具,而只是一个Fruit。它已丢失了作为一个Tomato(西红柿)的所有特征。另外,假如回收一个GreenZebra,ripen()和slice()会把它别离转换成一个Tomato和一个Fruit。所以很是不幸,如果想建造工具的一个当地副本,Java中的副本构建器便不是出格适合我们。
1. 为什么在C++的浸染比在Java中大?
副本构建器是C++的一个根基组成部门,因为它能自动发生工具的一个当地副本。但前面的例子确实证明白它不适合在Java中利用,为什么呢?在Java中,我们操控的一切对象都是句柄,而在C++中,却可以利用雷同于句柄的对象,也能直接通报工具。这时便要用到C++的副本构建器:只要想得到一个工具,并按值通报它,就可以复制工具。所以它在C++里能很好地事情,但应留意这套机制在Java里是很不通的,所以不要用它。