12.3 克隆的控制
12.3 克隆的控制
下面这个例子总结了克隆的各种实现方法,然后在层次结构中将其“关闭”: //: CheckCloneable.java // Checking to see if a handle can be cloned
// Can’t clone this because it doesn’t // override clone(): class Ordinary {}
// Overrides clone, but doesn’t implement // Cloneable: class WrongClone extends Ordinary { public Object clone() throws CloneNotSupportedException { return super.clone(); // Throws exception } }
// Does all the right things for cloning: class IsCloneable extends Ordinary implements Cloneable { public Object clone() throws CloneNotSupportedException { return super.clone(); } }
// Turn off cloning by throwing the exception: class NoMore extends IsCloneable { public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } }
class TryMore extends NoMore { public Object clone() throws CloneNotSupportedException { // Calls NoMore.clone(), throws exception: return super.clone(); } }
class BackOn extends NoMore { private BackOn duplicate(BackOn b) { // Somehow make a copy of b // and return that copy. This is a dummy // copy, just to make the point: return new BackOn(); } public Object clone() { // Doesn’t call NoMore.clone(): return duplicate(this); } }
// Can’t inherit from this, so can’t override // the clone method like in BackOn: final class ReallyNoMore extends NoMore {}
public class CheckCloneable { static Ordinary tryToClone(Ordinary ord) { String id = ord.getClass().getName(); Ordinary x = null; if(ord instanceof Cloneable) { try { System.out.println(“Attempting " + id); x = (Ordinary)((IsCloneable)ord).clone(); System.out.println(“Cloned " + id); } catch(CloneNotSupportedException e) { System.out.println( “Could not clone " + id); } } return x; } public static void main(String[] args) { // Upcasting: Ordinary[] ord = { new IsCloneable(), new WrongClone(), new NoMore(), new TryMore(), new BackOn(), new ReallyNoMore(), }; Ordinary x = new Ordinary(); // This won’t compile, since clone() is // protected in Object: //! x = (Ordinary)x.clone(); // tryToClone() checks first to see if // a class implements Cloneable: for(int i = 0; i < ord.length; i++) tryToClone(ord[i]); } } ///:~
第一个类
总之,如果希望一个类能够克隆,那么:
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(); } } ///:~
这个例子第一眼看上去显得有点奇怪。不同水果的质量肯定有所区别,但为什么只是把代表那些质量的数据成员直接置入
从中可以看出一个问题。在
- 为什么在
C++ 的作用比在Java 中大? 副本构造器是C++ 的一个基本构成部分,因为它能自动产生对象的一个本地副本。但前面的例子确实证明了它不适合在Java 中使用,为什么呢?在Java 中,我们操控的一切东西都是指针,而在C++ 中,却可以使用类似于指针的东西,也能直接传递对象。这时便要用到C++ 的副本构造器:只要想获得一个对象,并按值传递它,就可以复制对象。所以它在C++ 里能很好地工作,但应注意这套机制在Java 里是很不通的,所以不要用它。