9. 項目15 可変性を最小限にする
// 複素数を表すクラス
public finalclass Complex {
private final double re;
private final double im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
// 実数部分・虚数部分へのアクセサーを提供
// アクセサー のみ (対応する mutator を持たない)
public double realPart() { return re; }
public double imaginaryPart() { return im; }
// 4つの算術操作:足し算・引き算・掛け算・割り算を提供
public Complex add(Complex c) {
return new Complex(re + c.re, im + c.im);
}
(…以下省略)
•
新たなインスタンスを生成して返している (算術操作メソッドの部分)
– 関数的方法
• オペランドを変更することなく、関数を適用した結果を返す
10. 項目15 可変性を最小限にする
• 不変オブジェクト
– スレッドセーフ。同期を必要としない。
– 制限なく共有できる
• コピー(#cloneやコピーコンストラクタ)を行う必要が無い
– コピー元と同値なので
– 既存のクラスを再利用
• 頻繁に使用される値 → 定数を提供
public static final Complex ZERO = new Complex(0, 0);
public static final Complex ONE = new Complex(1, 0);
public static final Complex I = new Complex(0, 1);
– 頻繁に要求されるインスタンスをキャッシュするstatic ファクトリメソッドを提供できる(項目1)
–
内部も共有できる
• 【例】 BigInteger の negate メソッド
– 同じ大きさで反対の符号のBigIntegerを返す
– 大きさ=int[] をコピーする必要がない
public class BigInteger extends Number … {
final int signum; // -1 for negative, 0 for zero, or 1 for positive
final int[] mag;
…
public BigInteger negate() {
return new BigInteger(this.mag, -this.signum);
19. 項目17 継承のために設計および文章化する、
でなければ継承を禁止する
• [オーバライド可能なメソッド]を呼び出すメソッド
– 呼び出しに関する記述は「This implementation」で始まる
public abstract class AbstractCollection<E> implements Collection<E> {
…
public abstract Iterator<E> iterator();
…
/**
* {@inheritDoc}
*
* <p>This implementation iterates over the collection looking for the
* specified element. If it finds the element, it removes the element
* from the collection using the iterator's remove method.
*
*…
*/
public boolean remove(Object o) {
Iterator<E> it = iterator();
if (o==null) {
…
– #iterator をオーバライドすることによって、#remove に影響を与える事が
記述されている
20. 項目17 継承のために設計および文章化する、
でなければ継承を禁止する
• 継承のために設計されたクラス
– →サブクラスを書いてテスト
• 継承を可能にするための制約
– コンストラクタは、オーバライド可能なメソッドを呼び出してはいけない
public class Super {
// コンストラクタがオーバライド可能なメソッドを呼び出し
public Super() {
overrideMe();
}
public void overrideMe() {}
}
public final class Sub extends Super {
private final Date date; // Blank final, set by constructor
Sub() { date = new Date();} // コンストラクタ
@Override public void overrideMe() {
System.out.println(date);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
}
}
25. 項目18 abstract class よりも interface を選ぶ
•
骨格実装の例
– int[] を List<Integer> として見せる Adapter
– static factory の中に隠蔽されている anonymous class
static List<Integer> intArrayAsList(final int[] a) {
if (a == null)
throw new NullPointerException();
return new AbstractList<Integer>() {
public Integer get(int i) {
return a[i]; // Autoboxing (Item 5)
}
@Override public Integer set(int i, Integer val) {
int oldVal = a[i];
a[i] = val; // Auto-unboxing
return oldVal; // Autoboxing
}
public int size() {
return a.length;
}
};
}
• ※パフォーマンスはそれほど良いわけではない (boxing 、unboxing をしているため)
26. 項目18 abstract class よりも interface を選ぶ
•
Map.Entryの骨格実装例
public abstract class AbstractMapEntry<K,V> implements Map.Entry<K,V> {
// Primitive operations
public abstract K getKey();
public abstract V getValue();
// 変更可能な Mapの Entries は、このメソッドを override しなければならない
public V setValue(V value) {
throw new UnsupportedOperationException();
}
// Implements the general contract of Map.Entry.equals
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (! (o instanceof Map.Entry))
return false;
Map.Entry<?,?> arg = (Map.Entry) o;
return equals(getKey(), arg.getKey()) &&
equals(getValue(), arg.getValue());
}
…
}
28. 項目20 タグ付クラスよりクラス階層を選ぶ
// Tagged class - クラス階層より、かなり劣る
class Figure {
enum Shape { RECTANGLE, CIRCLE };
// Tag field - the shape of this figure
final Shape shape;
// These fields are used only if shape is RECTANGLE
double length;
double width;
// This field is used only if shape is CIRCLE
double radius;
// Constructor for circle
Figure(double radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}
// Constructor for rectangle
Figure(double length, double width) {
shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
29. 項目20 タグ付クラスよりクラス階層を選ぶ
double area() {
switch(shape) {
case RECTANGLE:
return length * width;
case CIRCLE:
return Math.PI * (radius * radius);
default:
throw new AssertionError();
}
}
}
• 欠点
– 決まりきったコードで散らかっている
• enum 宣言
• tag field
• switch 文
– メモリ量が増加
• 他の特性に属するフィールドを抱えているため
– 特性の追加時にソースの修正が必要
• switch 文 に case を追加しなければならない
30. 項目20 タグ付クラスよりクラス階層を選ぶ
• サブタイプ化
// クラス階層による置き換え
abstract class Figure {
abstract double area();
}
class Circle extends Figure {
final double radius;
Circle(double radius) { this.radius = radius; }
double area() { return Math.PI * (radius * radius); }
}
class Rectangle extends Figure {
final double length;
final double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
double area() { return length * width; }
}
•
各特性の実装に対して → 独自のクラスを定義
– 自分と無関係なデータフィールドを負っていない
•
全ての field が final
31. 項目21 戦略を表現するために関数オブジェクトを使用する
• java は 関数ポインタを提供していないが、オブジェクトへの参照を使用できる
– [他のオブジェクト]に対して操作を行うメソッドを持つオブジェクトを定義
•
•
•
•
他のオブジェクトがそのメソッドに明示的に渡される
そのようなメソッドを1つだけ公開
関数オブジェクト(function object)と呼ばれる
戦略(Strategy)パターンを実装
– 戦略を表すインタフェース(Strategy interface)
public interface Comparator<T> {
public int compare(T t1, T t2);
}
– 個々の具象戦略クラス(Concrete strategy class)
class StringLengthComparator implements Comparator<String> {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
» StringLengthComparatorの戦略:
• 辞書順ではなく、文字列の長さで順序付け
32. 項目21 戦略を表現するために関数オブジェクトを使用する
• Stateless
– シングルトンにするのに適している
class StringLengthComparator {
private StringLengthComparator() { }
public static final StringLengthComparator
INSTANCE = new StringLengthComparator();
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
• anonymous classで使用
Arrays.sort(stringArray, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
– → 呼び出し毎に新たなインスタンス生成
• 繰り返し実行する場合は private static final にして再利用を検討
33. 項目21 戦略を表現するために関数オブジェクトを使用する
• concrete strategy class は public にする必要はない
– “host class” の private nested class にできる
class Host {
private static class StrLenCmp implements Comparator<String>, Serializable {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
public static final Comparator<String>
STRING_LENGTH_COMPARATOR = new StrLenCmp();
…
}
– Stringクラス の CASE_INSENSITIVE_ORDER はこのパターン
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
…
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
public int compare(String s1, String s2) {
…
34. 項目22 非staticのメンバークラスより
staticのメンバークラスを選ぶ
• nested class
– 他のclass内に定義されたclass
– enclosing class に対して仕えるためだけに存在すべき
– 4種類
• static member class
• nonstatic member class
• anonymous class
• local class
• enclosing instance への参照が必要ならば nonstatic にする
• →そうでなければ、staticにする。
•
クラスがメソッド内に属していて、1箇所からのみインスタンスを生成する必要があり、
そのクラスを特徴づける型が存在すれば、anonymous class にする