私はEffective Java(第2版)の項目18を読み直していました。抽象クラスよりもインターフェースを好む。Map.Entry<K,V>
その項目では、Josh Bloch がインターフェイスのスケルトン実装の例を提供しています。
// Skeletal Implementation
public abstract class AbstractMapEntry<K,V>
implements Map.Entry<K,V> {
// Primitive operations
public abstract K getKey();
public abstract V getValue();
// ... remainder omitted
}
この例から 2 つの疑問が生じます。
- ここで getKey と getValue が抽象メソッドとして明示的に宣言されているのはなぜですか? これらはMap.Entryインターフェイスの一部であるため、抽象クラスで冗長な宣言を行う理由はわかりません。
Bloch氏が言及しているように、これらのプリミティブメソッドを抽象として残すというイディオムを使用するのはなぜですか? なぜこれをしないのですか:
// スケルトンの実装 public abstract class AbstractMapEntry は Map.Entry を実装します { 秘密 K キー; プライベート V 値;
// Primitive operations public K getKey() {return key;} public V getValue() {return value;} // ... remainder omitted
}
この利点は、各サブクラスが独自のフィールド セットを定義する必要がなく、アクセサーによってキーと値にアクセスできることです。サブクラスが本当にアクセサーの独自の動作を定義する必要がある場合は、Map.Entry インターフェイスを直接実装できます。もう 1 つの欠点は、スケルトン実装によって提供される equals メソッドで、抽象アクセサーが呼び出されることです。
// 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());
}
ブロックは、サブクラスによる変更に対してスーパークラスが脆弱になるため、継承用に設計されたクラスからオーバーライド可能なメソッド (項目 17) を呼び出すことに対して警告しています。これは意見の問題かもしれませんが、ブロッホは本の中でこれについて詳しく説明していないので、ストーリーにもっと多くのことがあるかどうかを判断したいと思っていました.