Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
clone( ), which throws an exception and prevents cloning.
Feedback
But what if the programmer doesn’t follow the “proper” path of calling
super.clone( ) inside the overridden clone( ) method? In BackOn,
you can see how this can happen. This class uses a separate method
duplicate( ) to make a copy of the current object and calls this method
inside clone( ) instead of calling super.clone( ). The exception is never thrown and the new class is cloneable. You can’t rely on throwing an
exception to prevent making a cloneable class. The only sure-fire solution
is shown in ReallyNoMore, which is final and thus cannot be inherited.
That means if clone( ) throws an exception in the final class, it cannot
be modified with inheritance and the prevention of cloning is assured.
(You cannot explicitly call Object.clone( ) from a class that has an
arbitrary level of inheritance; you are limited to calling super.clone( ),
which has access to only the direct base class.) Thus, if you make any
Appendix A: Passing & Returning Objects
1077
objects that involve security issues, you’ll want to make those classes
final. Feedback
The first method you see in class CheckCloneable is tryToClone( ),
which takes any Ordinary object and checks to see whether it’s cloneable
with instanceof. If so, it casts the object to an IsCloneable, calls
clone( ) and casts the result back to Ordinary, catching any exceptions
that are thrown. Notice the use of run-time type identification (see
Chapter 10) to print the class name so you can see what’s happening.
Feedback
In main( ), different types of Ordinary objects are created and upcast to
Ordinary in the array definition. The first two lines of code after that
create a plain Ordinary object and try to clone it. However, this code will
not compile because clone( ) is a protected method in Object. The
remainder of the code steps through the array and tries to clone each
object, reporting the success or failure of each. Feedback
So to summarize, if you want a class to be cloneable: Feedback
1.
Implement the Cloneable interface.
2.
Override clone( ).
3.
Call super.clone( ) inside your clone( ).
4.
Capture exceptions inside your clone( ).
This will produce the most convenient effects. Feedback
The copy constructor
Cloning can seem to be a complicated process to set up. It might seem like
there should be an alternative. One approach is to use serialization, as
shown earlier. Another approach that might occur to you (especially if
you’re a C++ programmer) is to make a special constructor whose job it is
to duplicate an object. In C++, this is called the copy constructor. At first,
this seems like the obvious solution, but in fact it doesn’t work. Here’s an
example:
//: appendixa:CopyConstructor.java
// A constructor for copying an object of the same
1078
Thinking in Java
www.BruceEckel.com
// type, as an attempt to create a local copy.
import com.bruceeckel.simpletest.*;
import java.lang.reflect.*;
class FruitQualities {
private int weight;
private int color;
private int firmness;
private int ripeness;
private int smell;
// etc.
public FruitQualities() { // Default constructor
// Do something meaningful...
}
// Other constructors:
// ...
// Copy constructor:
public FruitQualities(FruitQualities f) {
weight = f.weight;
color = f.color;
firmness = f.firmness;
ripeness = f.ripeness;
smell = f.smell;
// etc.
}
}
class Seed {
// Members...
public Seed() { /* Default constructor */ }
public Seed(Seed s) { /* Copy constructor */ }
}
class Fruit {
private FruitQualities fq;
private int seeds;
private Seed[] s;
public Fruit(FruitQualities q, int seedCount) {
fq = q;
seeds = seedCount;
s = new Seed[seeds];
for(int i = 0; i < seeds; i++)
s[i] = new Seed();
}
Appendix A: Passing & Returning Objects
1079
// Other constructors:
// ...
// Copy constructor:
public Fruit(Fruit f) {
fq = new FruitQualities(f.fq);
seeds = f.seeds;
s = new Seed[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 {
public Tomato() {
super(new FruitQualities(), 100);
}
public Tomato(Tomato t) { // Copy-constructor
super(t); // Upcast for base copy-constructor
// Other copy-construction activities...
}
}
class ZebraQualities extends FruitQualities {
private int stripedness;
public ZebraQualities() { // Default constructor
super();
// do something meaningful...
}
public ZebraQualities(ZebraQualities z) {
super(z);
stripedness = z.stripedness;
}
}
1080
Thinking in Java
www.BruceEckel.com
class GreenZebra extends Tomato {
public GreenZebra() {
addQualities(new ZebraQualities());
}
public GreenZebra(GreenZebra g) {
super(g); // Calls Tomato(Tomato)
// Restore the right qualities: