SE450: Lecture 8
(Object Creation)
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package subclass.constructor;
class Main {
public static void main(String[] argv) {
new X();
new X();
}
}
class O {
int i, j;
{ i = 42; System.err.println("i"); }
O() { System.err.println("O"); }
{ j = 27; System.err.println("j"); }
}
class X extends O {
int k;
X() { System.err.println("X"); }
{ k = 27; System.err.println("k"); }
}
|
|
|
Don't let the "this" reference escape during construction
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package subclass.ex8;
public class M {
public static void main(String[] argv) {
new B();
new C();
}
}
class A {
A() {System.out.println("A()");}
}
class B extends A {
B() {
this(1);
System.out.println("B()");
}
B(int x) {System.out.println("B(int)");}
}
class C extends B {
C() {System.out.println("C()");}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package subclass.ex7;
public class M {
public static void main(String[] argv) {
new B(1);
new C();
}
}
class A {
A(int i) {System.out.println("A(int)");}
}
class B extends A {
B(int i) {
super(i);
System.out.println("B(int)");
}
}
class C extends B {
// The following will not compile!
// C() {System.out.println("C");}
C() {
super(0);
System.out.println("C");
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package clone.deepVshallow;
public class Main {
public static void main(String[] argv) throws CloneNotSupportedException {
Node w = new Node(1, new Node(2, new Node(3)));
Node v = w;
Node x = (Node) w.shallow_copy();
Node y = (Node) w.deep_copy();
Node z = (Node) w.shallow_clone();
System.out.println("w==v: " + ((w == v) ? "true" : "false"));
System.out.println("w==x: " + ((w == x) ? "true" : "false"));
System.out.println("w==y: " + ((w == y) ? "true" : "false"));
System.out.println("w==z: " + ((w == z) ? "true" : "false"));
System.out.println("w.equals(v): " + ((w.equals(v)) ? "true" : "false"));
System.out.println("w.equals(x): " + ((w.equals(x)) ? "true" : "false"));
System.out.println("w.equals(y): " + ((w.equals(y)) ? "true" : "false"));
System.out.println("w.equals(z): " + ((w.equals(z)) ? "true" : "false"));
System.out.println("w.shallow_equals(v): " +
((w.shallow_equals(v)) ? "true" : "false"));
System.out.println("w.shallow_equals(x): " +
((w.shallow_equals(x)) ? "true" : "false"));
System.out.println("w.shallow_equals(y): " +
((w.shallow_equals(y)) ? "true" : "false"));
System.out.println("w.shallow_equals(z): " +
((w.shallow_equals(z)) ? "true" : "false"));
System.out.println("w.deep_equals(v): " +
((w.deep_equals(v)) ? "true" : "false"));
System.out.println("w.deep_equals(x): " +
((w.deep_equals(x)) ? "true" : "false"));
System.out.println("w.deep_equals(y): " +
((w.deep_equals(y)) ? "true" : "false"));
System.out.println("w.deep_equals(z): " +
((w.deep_equals(z)) ? "true" : "false"));
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package clone.deepVshallow;
class Node implements Cloneable {
private int i;
private Node next;
public Node(int i, Node next) { this.i = i; this.next = next; }
public Node(int i) { this(i,null); }
public Object shallow_copy() {
return new Node(i, next);
}
public Object shallow_clone() throws CloneNotSupportedException {
return super.clone();
}
public boolean shallow_equals(Object o) {
if (!(this.getClass().equals(o.getClass())))
return false;
Node that = (Node) o;
return (i == that.i) && (next == that.next);
}
public Object deep_copy() {
Node next_copy = (next==null) ? null : (Node) next.deep_copy();
return new Node(i, next_copy);
}
public Object deep_clone() throws CloneNotSupportedException {
Node result = (Node) super.clone();
result.next = (next==null) ? null : (Node) next.deep_clone();
return result;
}
public boolean deep_equals(Object o) {
if (!(this.getClass().equals(o.getClass())))
return false;
Node that = (Node) o;
return (i == that.i)
&& ((next==null) ? (that.next==null) : next.deep_equals(that.next));
}
}
|
|
|
An alternative is to use a copy method or copy constructor,
but these do have the same behavior with respect to
subclassing.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package clone.magic;
public class Main {
public static void main(String[] argv) throws CloneNotSupportedException {
B x = new B(42,27);
System.out.println(x);
System.out.println(new A(x));
System.out.println(x.copy());
System.out.println(x.clone());
}
}
class A implements Cloneable {
int i;
public A(int i) { this.i = i; }
// A copy constructor
public A(A that) { this.i = that.i; }
// A copy method
public Object copy() { return new A(i); }
// The clone method
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String toString() { return "A("+i+")"; }
}
class B extends A {
int j;
public B(int i, int j) { super(i); this.j = j; }
public String toString() { return "B("+i+","+j+")"; }
}
|
|
|
Output is:
B(42,27)
A(42)
A(42)
B(42,27)
|
The intent is that
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
The last intent is not a requirement, but is generally true.
By convention, you obtain the returned object by calling
super.clone()
. If all superclasses do this,
then x.clone().getClass() == x.getClass()
will
be true.
Object
doesn't implement Cloneable
itself. (Object.clone
is protected.)
The Cloneable
interface is meant to be a
mixin interface, however it doesn't do a very good
job of this.
Parts of Java that support cloning:
-
the empty
Cloneable
interface
(note the misspelling)
-
The
Object.clone
method (or your superclass's
clone method)
-
CloneNotSupportedException
, to signal the
clone method shouldn't have been called.
You can:
-
Support clone (implement
Cloneable
and declare
clone()
to throw no exceptions).
-
Conditionally support clone (implement
Clonable
, but may throw
CloneNotSupportedException
). This is the
case for a container class whose contents may or may not
be clonable.
-
Allow subclasses to support clone (do not implement
Clonable
; do nothing, or override
clone
to do the right thing for your class)
-
Forbid clone (do not implement
Clonable
;
override clone
to always throw
CloneNotSupportedException
)
To disallow cloning, make you class final
and
do not override clone()
.
public final class A { ... }
If your class is non-final and public, you either need to
expect that a subclass will implement clone, or disallow
cloning:
public class A {
...
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
To implement cloning:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package clone.stack;
public final class IntegerStack implements Cloneable {
private int[] buffer;
private int top;
public IntegerStack(int maxContents) {
buffer = new int[maxContents];
top = -1;
}
public void push(int val) {
buffer[++top] = val;
}
public int pop() {
return buffer[top--];
}
public Object clone() {
try {
IntegerStack result = (IntegerStack) super.clone();
result.buffer = buffer.clone();
return result;
} catch (CloneNotSupportedException e) {
// cannot happen
throw new RuntimeException(e);
}
}
}
|
|
|
For mutable objects, one might consider implementing a
factory by cloning prototype objects, rather than calling a
constructor each time.
Here is a static factory using constructors:
dir:factory/person [source]
Here it is refactored to use prototypes:
dir:clone/prototype [source]
In most cases, the constructor version is preferable in Java.
An object which implements the Serializable
interface can be written and read from an
OutputStream
and InputStream
.
These stream objects must be wrapped inside either an
ObjectOutputStream
or
ObjectInputStream
.
These classes contain methods
writeObject(Object):void
and
readObject():Object
.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package serialization;
import java.io.*;
public class Main1 {
public static void main(String args[]) {
try {
ObjectOutputStream os
= new ObjectOutputStream (new FileOutputStream("out.dat"));
os.writeObject(new Entry("Save Me", 1));
os.close();
ObjectInputStream is
= new ObjectInputStream (new FileInputStream("out.dat"));
Object o = is.readObject();
is.close();
Entry e = (Entry) o;
System.out.println("Entry restored from file is: " + e.toString());
} catch (Exception e) { e.printStackTrace(); }
}
}
class Entry implements Serializable {
private static final long serialVersionUID = 2008L;
private String message = "";
private int messageNumber = 0;
public Entry(String message, int messageNumber) {
this.message = message;
this.messageNumber = messageNumber;
}
public String getMessage() {
return message;
}
public int getMessageNumber() {
return messageNumber;
}
public String toString() {
return message + " " + Integer.toString(messageNumber);
}
}
|
|
|
Objects will usually contain references to other objects.
Serialization writes out all referenced objects (and any
objects they reference).
Effectively, it writes out the whole graphs of objects.
All referenced classes must be serializable for this to
work.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package serialization;
import java.io.*;
public class Main2 {
public static void main(String args[]) {
try {
Person person = new Person("Matt", 30);
PersonEntry entry = new PersonEntry(person, 1);
ObjectOutputStream os
= new ObjectOutputStream (new FileOutputStream("out.dat"));
os.writeObject(entry);
os.close();
ObjectInputStream is
= new ObjectInputStream (new FileInputStream("out.dat"));
Object o = is.readObject();
is.close();
PersonEntry entry2 = (PersonEntry) o;
System.out.println("Entry restored from file is" + entry2.toString());
} catch (Exception e) { e.printStackTrace(); }
}
}
class PersonEntry implements Serializable {
private static final long serialVersionUID = 2008L;
private Person person = null;
private int personNumber = 0;
public PersonEntry(Person person, int personNumber) {
this.person = person;
this.personNumber = personNumber;
}
public Person getPerson() {
return person;
}
public int getPersonNumber() {
return personNumber;
}
public String toString() {
return person.toString() + " Number " + Integer.toString(personNumber);
}
}
@SuppressWarnings("serial")
class Person implements Serializable {
private String name = "";
private int age = 0;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String toString() {
return "Name: " + name + " Age: " + Integer.toString(age);
}
}
|
|
|
If fields reference a non-serializable object, declare those
fields transient
.
Transient fields are not written when the object is
serialized (written).
Transient fields are set to null
when the
object is deserialized (read).
Control over which refered objects are stored and how they
are reconstructed.
Implement the Externalizable
interface. This
interface allows you to provide the methods
writeExternal(ObjectOutput):void
and
readExternal(ObjectOutput)
.
You must provide a no-argument constructor as this is called
when readExternal()
is used to reconstruct
objects.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package serialization;
import java.io.*;
class Person2 implements Externalizable {
private static final long serialVersionUID = 2008L;
private String name = ""; private int age = 0;
public Person2() { }
public Person2(String name, int age) { this.name = name; this.age = age;}
public String getName() { return name; }
public int getAge() { return age; }
public String toString() {return "Name: " + name + " Age: " + Integer.toString(age);}
public void writeExternal(ObjectOutput out)
throws IOException
{
out.writeObject(name);
out.writeInt(age);
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
name = (String) in.readObject();
age = in.readInt();
}
}
|
|
|
Mark your class as final
and do not implement
Serializable
.
|
01
02
03
04
05
06
07
|
package singleton.staticClass;
public class S {
static private int i;
private S() {}
static public int inc() { return i++; }
}
|
|
|
|
01
02
03
04
05
06
07
08
|
package singleton.staticClass.main;
import singleton.staticClass.S;
public class Main {
public static void main (String[] args) {
System.out.println(S.inc());
System.out.println(S.inc());
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package singleton.state;
public class S {
private S() {}
static private SState state;
static {
if ("linux".equals(System.getProperty("os.name"))) {
state = new SLinux();
} else {
state = new SOther();
}
}
static public int inc() { return state.inc(); }
static private interface SState {
public int inc();
}
static private class SLinux implements SState {
private int i;
public int inc() {return ++i;}
}
static private class SOther implements SState {
private int i;
public int inc() {return --i;}
}
}
|
|
|
|
01
02
03
04
05
06
07
08
|
package singleton.state.main;
import singleton.state.S;
public class Main {
public static void main (String[] args) {
System.out.println(S.inc());
System.out.println(S.inc());
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package singleton.lazy;
public class S {
private S() {}
static private SState state;
static public int inc() {
if (state == null) {
if ("linux".equals(System.getProperty("os.name"))) {
state = new SLinux();
} else {
state = new SOther();
}
}
return state.inc();
}
static private interface SState {
public int inc();
}
static private class SLinux implements SState {
private int i;
public int inc() {return ++i;}
}
static private class SOther implements SState {
private int i;
public int inc() {return --i;}
}
}
|
|
|
|
01
02
03
04
05
06
07
08
|
package singleton.lazy.main;
import singleton.lazy.S;
public class Main {
public static void main (String[] args) {
System.out.println(S.inc());
System.out.println(S.inc());
}
}
|
|
|
In dynamically initialized languages, lazy instantiation is
usually silly.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
package singleton.pub;
public interface S {
public static final S instance =
("linux".equals(System.getProperty("os.name"))) ? new SLinux() : new SOther();
public int inc();
}
final class SLinux implements S {
private int i;
public int inc() {return ++i;}
}
final class SOther implements S {
private int i;
public int inc() {return --i;}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
|
package singleton.pub.main;
import singleton.pub.S;
public class Main {
public static void main (String[] args) {
S s = S.instance;
System.out.println(s.inc());
System.out.println(s.inc());
}
}
|
|
|
If you make the singleton field public, then it should be
final.
If a field is final, then it must be assigned in the
initializer.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package singleton.pub2;
public interface S {
public static final S instance = SFactory.newS();
public int inc();
}
class SFactory {
static S newS() {
if ("linux".equals(System.getProperty("os.name")))
return new SLinux();
else
return new SOther();
}
}
final class SLinux implements S {
private int i;
public int inc() {return ++i;}
}
final class SOther implements S {
private int i;
public int inc() {return --i;}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
|
package singleton.pub2.main;
import singleton.pub2.S;
public class Main {
public static void main (String[] args) {
S s = S.instance;
System.out.println(s.inc());
System.out.println(s.inc());
}
}
|
|
|
Sometimes you can't do everything in an initializer
expression. In this case, you may need a static method.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package singleton.pub3;
public abstract class S {
private static S newS() {
if ("linux".equals(System.getProperty("os.name")))
return new SLinux();
else
return new SOther();
}
public static final S instance = newS();
public abstract int inc();
}
final class SLinux extends S {
private int i;
public int inc() {return ++i;}
}
final class SOther extends S {
private int i;
public int inc() {return --i;}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
|
package singleton.pub3.main;
import singleton.pub3.S;
public class Main {
public static void main (String[] args) {
S s = S.instance;
System.out.println(s.inc());
System.out.println(s.inc());
}
}
|
|
|
It's a bit more natural using an abstract class.
(I dislike java's restrictions on static members in
interfaces.)
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
package singleton.pub4;
public abstract class S {
private static S instance;
static {
if ("linux".equals(System.getProperty("os.name")))
instance = new SLinux();
else
instance = new SOther();
}
public static S get() {return instance;}
public abstract int inc();
}
final class SLinux extends S {
private int i;
public int inc() {return ++i;}
}
final class SOther extends S {
private int i;
public int inc() {return --i;}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
|
package singleton.pub4.main;
import singleton.pub4.S;
public class Main {
public static void main (String[] args) {
S s = S.get();
System.out.println(s.inc());
System.out.println(s.inc());
}
}
|
|
|
You can also use a public method rather than a public field.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package singleton.classloading;
/**
* This example shows that you need to be careful to ensure that
* singletons are not accessed before they are created!
*/
class S {
private static S instance;
public static int numInstances;
public static S getInstance() {
if (instance == null) {
instance = new S();
}
return instance;
}
private S() {
S.numInstances++;
B.doNothing(); // B is loaded here, during A's constructor
}
}
class B {
// B caches the only instance of S
public static S a = S.getInstance();
public static void doNothing() {}
}
class Main {
public static void main(String[] args) {
System.out.println(S.getInstance() == B.a); // false!
System.out.println(S.numInstances); // 2!
}
}
|
|
|
Dissallow cloning in singleton classes.
final class SLinux implements S {
// ...
private Object readResolve() throws java.io.ObjectStreamException {
return SObject.get();
}
}
final class SOther implements S {
// ...
private Object readResolve() throws java.io.ObjectStreamException {
return SObject.get();
}
}
readResolve()
is called after an object is
deserialized.
Normally readResolve()
cleans up the object a
little and returns this
, but here we want it to
throw away the deserialized object.
See http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html
Enums are java.lang.Comparable and
java.io.Serializable.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package enumeration2;
import java.util.ArrayList;
import java.util.List;
public class Card {
public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX,
SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE }
public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }
private final Rank rank;
private final Suit suit;
private Card(Rank rank, Suit suit) {
this.rank = rank;
this.suit = suit;
}
public Rank rank() { return rank; }
public Suit suit() { return suit; }
public String toString() { return rank + " of " + suit; }
private static final List<Card> protoDeck = new ArrayList<Card>();
// Initialize prototype deck
static {
for (Suit suit : Suit.values())
for (Rank rank : Rank.values())
protoDeck.add(new Card(rank, suit));
}
public static List<Card> newDeck() {
return new ArrayList<Card>(protoDeck); // Return copy of prototype deck
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package enumeration2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
private Main() {}
public static void main(String args[]) {
int numHands = Integer.parseInt(args[0]);
int cardsPerHand = Integer.parseInt(args[1]);
List<Card> deck = Card.newDeck();
Collections.shuffle(deck);
for (int i=0; i < numHands; i++)
System.out.println(deal(deck, cardsPerHand));
}
public static List<Card> deal(List<Card> deck, int n) {
int deckSize = deck.size();
List<Card> handView = deck.subList(deckSize-n, deckSize);
List<Card> hand = new ArrayList<Card>(handView);
handView.clear();
return hand;
}
}
|
|
|
Here are Java 1.4 versions:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package enumeration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
// Ordinal-based typesafe enum (From Bloch)
//
// No need to override equals, since only one instance of each suit.
// Note that instanceNum and name are instance fields.
// Note that cNumInstances is a class field.
// Note that the order of the constant definitions is important.
// Note that VALUES is an immutable collection.
// Java arrays are always mutable :-(
public final class Suit implements Comparable<Suit> {
// Number of instances
private static int cNumInstances = 0;
// Ordinal for this instance
private final int instanceNum;
// Name of Suit
private final String name;
// Private constructor: All instances created in the class
private Suit(String name) {
this.name = name;
this.instanceNum = Suit.cNumInstances++;
}
public String toString() {
return name;
}
public int compareTo(Suit that) {
return this.instanceNum - that.instanceNum;
}
public static final Suit CLUBS = new Suit("clubs");
public static final Suit DIAMONDS = new Suit("diamonds");
public static final Suit HEARTS = new Suit("hearts");
public static final Suit SPADES = new Suit("spades");
public static final List<Suit> VALUES;
static {
Suit[] values = { CLUBS, DIAMONDS, HEARTS, SPADES };
VALUES = Collections.unmodifiableList(Arrays.asList(values));
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package enumeration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
// Ordinal-based typesafe enum (From Bloch)
//
// No need to override equals, since only one instance of each rank.
// Note that instanceNum and name are instance fields.
// Note that cNumInstances is a class field.
// Note that the order of the constant definitions is important.
// Note that VALUES is an immutable collection.
// Java arrays are always mutable :-(
public final class Rank implements Comparable<Rank> {
// Number of instances
private static int cNumInstances = 0;
// Ordinal for this instance
private final int instanceNum;
// Name of Rank
private final String name;
// Private constructor: All instances created in the class
private Rank(String name) {
this.name = name;
instanceNum = Rank.cNumInstances++;
}
public String toString() {
return name;
}
public int compareTo(Rank that) {
return this.instanceNum - that.instanceNum;
}
public int getValue() {
if (this == ACE_HIGH) {
return 1;
} else {
return instanceNum + 1;
}
}
public static final Rank ACE_LOW = new Rank("ace");
public static final Rank TWO = new Rank("two");
public static final Rank THREE = new Rank("three");
public static final Rank FOUR = new Rank("four");
public static final Rank FIVE = new Rank("five");
public static final Rank SIX = new Rank("six");
public static final Rank SEVEN = new Rank("seven");
public static final Rank EIGHT = new Rank("eight");
public static final Rank NINE = new Rank("nine");
public static final Rank TEN = new Rank("ten");
public static final Rank JACK = new Rank("jack");
public static final Rank QUEEN = new Rank("queen");
public static final Rank KING = new Rank("king");
public static final Rank ACE_HIGH = new Rank("ace");
public static final List<Rank> VALUES;
static {
Rank[] values = {
ACE_LOW, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE_HIGH
};
VALUES = Collections.unmodifiableList(Arrays.asList(values));
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package enumeration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Typesafe enum (From Bloch)
//
public final class Card {
// Rank of Card
private final Rank rank;
// Suit of Card
private final Suit suit;
// Private constructor: All instances created in the class
private Card(Rank rank, Suit suit) {
this.rank = rank;
this.suit = suit;
}
public String toString() {
return rank + " of " + suit;
}
public int compareRank(Card c) {
return rank.compareTo(c.rank);
}
public int compareSuit(Card c) {
return suit.compareTo(c.suit);
}
public Rank getRank() {
return rank;
}
public int getRankValue() {
return rank.getValue();
}
public Suit getSuit() {
return suit;
}
public static final List<Card> VALUES;
static {
List<Card> values = new ArrayList<Card>(56);
for (Suit s : Suit.VALUES) {
for (Rank r : Rank.VALUES) {
values.add(new Card(r, s));
}
}
VALUES = Collections.unmodifiableList(values);
}
}
|
|
|
Recall the code we had for expression trees:
file:ExprFactory.java [source] [doc-public] [doc-private]
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
package enumeration2;
public enum Op {
ADD("+") { public int eval(int x, int y) { return x + y; } },
SUB("-") { public int eval(int x, int y) { return x - y; } },
MUL("*") { public int eval(int x, int y) { return x * y; } },
DIV("/") { public int eval(int x, int y) { return x / y; } };
private final String name;
private Op(String name) { this.name = name; }
public String toString() { return name; }
public abstract int eval(int x, int y);
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
package enumeration2;
public class ExprFactory {
private ExprFactory() {}
static public Expr newConst(int v) {
return new Const(v);
}
static public Expr newPlus(Expr l, Expr r) {
return new BinOp(l, Op.ADD, r);
}
static public Expr newMinus(Expr l, Expr r) {
return new BinOp(l, Op.SUB, r);
}
static public Expr newMult(Expr l, Expr r) {
return new BinOp(l, Op.MUL, r);
}
static public Expr newQuot(Expr l, Expr r) {
return new BinOp(l, Op.DIV, r);
}
}
|
|
|
Here is the Java 1.4 version:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package enumeration;
public abstract class Op {
private final String name;
Op(String name) { this.name = name; }
public String toString() { return name; }
public abstract int eval(int x, int y);
public static final Op ADD = new OpAdd();
public static final Op SUB = new OpSub();
public static final Op MUL = new OpMul();
public static final Op DIV = new OpDiv();
}
final class OpAdd extends Op {
OpAdd() { super("+"); }
public int eval(int x, int y) { return x+y; }
}
final class OpSub extends Op {
OpSub() { super("-"); }
public int eval(int x, int y) { return x-y; }
}
final class OpMul extends Op {
OpMul() { super("*"); }
public int eval(int x, int y) { return x*y; }
}
final class OpDiv extends Op {
OpDiv() { super("/"); }
public int eval(int x, int y) { return x/y; }
}
|
|
|
The simplest version exports the concrete class names.
|
01
02
03
04
05
|
package factory.shape1;
import java.awt.Graphics;
public interface Shape {
void paint(Graphics g);
}
|
|
|
|
01
02
03
04
05
06
|
package factory.shape1;
import java.awt.Graphics;
public class Circle implements Shape {
public void paint(Graphics g) { /* ... */ }
// ...
}
|
|
|
|
01
02
03
04
05
06
|
package factory.shape1;
import java.awt.Graphics;
public class Square implements Shape {
public void paint(Graphics g) { /* ... */ }
// ...
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
|
package factory.shape1.main;
import factory.shape1.Shape;
import factory.shape1.Square;
import factory.shape1.Circle;
public class Main {
public static void main (String[] args) {
Shape[] a = new Shape[2];
a[0] = new Circle();
a[1] = new Square();
}
}
|
|
|
A factory allows the concrete class names to remain hidden.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
package factory.shape2;
import java.awt.Graphics;
public interface Shape {
void paint(Graphics g);
}
class Circle implements Shape {
public void paint(Graphics g) { /* ... */ }
// ...
}
class Square implements Shape {
public void paint(Graphics g) { /* ... */ }
// ...
}
|
|
|
|
01
02
03
04
05
06
07
08
09
|
package factory.shape2;
public class ShapeFactory {
private ShapeFactory() {}
static public Shape newInstance(String selector) {
if ("Circle".equals(selector)) return new Circle();
if ("Square".equals(selector)) return new Square();
throw new IllegalArgumentException();
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
|
package factory.shape2.main;
import factory.shape2.Shape;
import factory.shape2.ShapeFactory;
public class Main {
public static void main (String[] args) {
Shape[] a = new Shape[2];
a[0] = ShapeFactory.newInstance("Circle");
a[1] = ShapeFactory.newInstance("Square");
}
}
|
|
|
Changing names does not affect client code.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
package factory.shape3;
import java.awt.Graphics;
public interface Shape {
void paint(Graphics g);
}
class Ellipse implements Shape {
public void paint(Graphics g) { /* ... */ }
// ...
}
class Rectangle implements Shape {
public void paint(Graphics g) { /* ... */ }
// ...
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
|
package factory.shape3;
public class ShapeFactory {
private ShapeFactory() {}
static public Shape newInstance(String selector) {
if ("Ellipse".equals(selector)) return new Ellipse();
if ("Circle".equals(selector)) return new Ellipse();
if ("Rectangle".equals(selector)) return new Rectangle();
if ("Square".equals(selector)) return new Rectangle();
throw new IllegalArgumentException();
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
|
package factory.shape3.main;
import factory.shape3.Shape;
import factory.shape3.ShapeFactory;
public class Main {
public static void main (String[] args) {
Shape[] a = new Shape[2];
a[0] = ShapeFactory.newInstance("Circle");
a[1] = ShapeFactory.newInstance("Square");
}
}
|
|
|
An alternative is to use multiple factory methods.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
package factory.shape4;
import java.awt.Graphics;
public interface Shape {
void paint(Graphics g);
}
class Ellipse implements Shape {
Ellipse(int radius1, int radius2) { /* ... */ }
public void paint(Graphics g) { /* ... */ }
// ...
}
class Rectangle implements Shape {
Rectangle(int height, int width) { /* ... */ }
public void paint(Graphics g) { /* ... */ }
// ...
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
package factory.shape4;
public class ShapeFactory {
private ShapeFactory() {}
static public Shape newEllipse(int radius1, int radius2) {
return new Ellipse(radius1, radius2);
}
static public Shape newCircle(int radius) {
return new Ellipse(radius, radius);
}
static public Shape newRectangle(int height, int width) {
return new Rectangle(height, width);
}
static public Shape newSquare(int height) {
return new Rectangle(height, height);
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
|
package factory.shape4.main;
import factory.shape4.Shape;
import factory.shape4.ShapeFactory;
public class Main {
public static void main (String[] args) {
Shape[] a = new Shape[2];
a[0] = ShapeFactory.newCircle(1);
a[1] = ShapeFactory.newSquare(1);
}
}
|
|
|
This is a Polymorphic version of the Static Factory Class
described erlier.
For example, an abstract factory is used to create GUI
widgets appropriate for linux (on linux machine) or windows
(on a windows machine).
Abstract Factories are almost always Singletons.
This is called Abstract Factory
by GoF.
The object created depends upon the actual type of the
creator.
This is called Factory Method
by GoF.
The polymorphic factory method pattern is used to connect
two hierarchies. A good example is
java.util.Collection and
java.util.Iterator.
Here is a silly example, using triples rather than general collections:
|
01
02
03
04
05
06
07
08
09
10
11
|
package factory.factorymethod;
public class Main {
public static void main (String[] args) {
Triple<String> t1 = new FieldTriple<String>("dog", "cat", "pig");
Triple<String> t2 = new ArrayTriple<String>("daisy", "rose", "tulip");
for (String s : t1)
System.out.println(s);
for (String s : t2)
System.out.println(s);
}
}
|
|
|
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package factory.factorymethod;
import java.util.Iterator;
import java.util.ArrayList;
interface Triple<E> extends Iterable<E> { }
class FieldTriple<E> implements Triple<E> {
private E one; E two; E three;
public FieldTriple(E one, E two, E three) {
this.one = one; this.two = two; this.three = three;
}
public Iterator<E> iterator() { return new TheIterator(); }
private class TheIterator implements Iterator<E> {
private int i;
public boolean hasNext() { return i < 3; }
public E next() {
i++;
switch (i) {
case 1: return one;
case 2: return two;
case 3: return three;
}
throw new java.util.NoSuchElementException();
}
public void remove() { throw new UnsupportedOperationException(); }
}
}
// Arrays do not play nicely with generics, so use ArrayList
class ArrayTriple<E> implements Triple<E> {
private ArrayList<E> a = new ArrayList<E>();
public ArrayTriple(E one, E two, E three) {
a.add(0,one); a.add(1,two); a.add(2,three);
}
public Iterator<E> iterator() { return new TheIterator(); }
private class TheIterator implements Iterator<E> {
private int i = -1;
public boolean hasNext() { return i < 2; }
public E next() {
i++;
if (i <= 2)
return a.get(i);
else
throw new java.util.NoSuchElementException();
}
public void remove() { throw new UnsupportedOperationException(); }
}
}
|
|
|
RequestProcessorFactoryFactory
It is sometimes an irritation to have more than one copy of
an immutable object.
If we only had one copy of each object, for example, we
could use ==
instead of equals
.
This is sometimes refered to as a hash cons, or
hash consing (LISP terminology).
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package flyweight;
import java.util.HashMap;
public class Data {
private Data() {}
private static HashMap<Integer,Video> hashmap = new HashMap<Integer,Video>();
private static int hash3 (Object key1, Object key2, Object key3) {
return key1.hashCode () + 5 * key2.hashCode () + 13 * key3.hashCode ();
}
/**
* Creates and manages flyweight objects. Ensures that flyweights
* are shared properly. When a client requests a flyweight, the
* Flyweight Factory object supplies an existing instance or
* creates one, if none exists.
*/
static public Video getVideo(String title, int year, String director) {
if ( (title == null)
|| (director == null)
|| (year <= 1800)
|| (year >= 5000)) {
throw new IllegalArgumentException();
}
title = title.trim();
director = director.trim();
if ( ("".equals(title))
|| ("".equals(director))) {
throw new IllegalArgumentException();
}
Integer key = hash3(title, year, director);
Video v = hashmap.get(key);
if ( (v == null)
|| !(v.title().equals(title))
|| (v.year() != year)
|| !(v.title().equals(title))) {
v = new VideoObj(title, year, director);
hashmap.put(key, v);
}
return v;
}
}
|
|
|
Revised: 2008/03/11 14:51