2

私は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 つの疑問が生じます。

  1. ここで getKey と getValue が抽象メソッドとして明示的に宣言されているのはなぜですか? これらはMap.Entryインターフェイスの一部であるため、抽象クラスで冗長な宣言を行う理由はわかりません。
  2. 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) を呼び出すことに対して警告しています。これは意見の問題かもしれませんが、ブロッホは本の中でこれについて詳しく説明していないので、ストーリーにもっと多くのことがあるかどうかを判断したいと思っていました.

4

4 に答える 4

1

Bloch は、サブクラスによる変更に対してスーパークラスが脆弱になるため、継承用に設計されたクラスからオーバーライド可能なメソッド (項目 17) を呼び出すことに対して警告しています。

彼は、他のメソッドではなく、コンストラクターでオーバーライド可能なメソッドを呼び出すことについて警告しています。

于 2010-12-13T07:18:31.690 に答える
1
  1. コンパイラーに教えてもらうのではなく、具体的なクラスが何を処理しようとしているのかを強調するのに役立つと思います (または、何が欠けているかを確認するために両方を比較する必要があります)。一種の自己文書化コード。しかし、それは確かに必要ではありません。私が見る限り、それはスタイルの問題です.
  2. これらの値を返すには、単純なゲッターと設定よりも重要なロジックがあります。標準の JDK(1.5) でチェックしたすべてのクラスは、メソッドの少なくとも 1 つで単純ではないことを行っていました。問題は自分で。

equals の問題に関しては、問題がオーバーライド可能であるため、抽象クラスがそれらを実装しても何も変わりませ。この場合、イコールは実装を予測するために慎重に実装しようとしていると言えます。通常、equals は、共分散の問題により、それ自体とそのサブクラスの間で true を返すように実装されるべきではありません (ただし、そうするものはたくさんあります) (スーパークラスはそれがサブクラスと等しいと考えますが、サブクラスはそれがスーパークラスと等しいとは考えません)。であるため、この種の equals の実装は、何をするにしてもトリッキーです。

于 2009-06-04T00:01:08.157 に答える
0

AbstractMapEntry#getKeygetValueが抽象的である(つまり実装されていない)理由の1つMap.Entryは、への内部インターフェースであるということMapです。ネストされたクラス/インターフェースの使用は、Javaが構成を実装する方法です。作曲の考え方は、作曲された部分は一流の概念ではないということです。むしろ、構成された部分は、それが全体に含まれている場合にのみ意味があります。この場合、合成パーツはMap.Entryであり、コンポジットのルートオブジェクトはですMap。明らかに、表現された概念は、aMapには多くMap.Entryのsがあるということです。

したがって、とのセマンティクスはAbstractMapEntry#getKey、基本的に、私たちが話しているgetValue実装に依存します。Mapあなたが書いたような単純な古いゲッターの実装は、のためにうまく機能しHashMapます。ConcurrentHashMapスレッドセーフが要求されるようなものでは機能しません。ConcurrentHashMapの実装getKeygetValue防御コピーの作成が行われる可能性があります。(自分でソースコードを確認することをお勧めします)。

getKey実装しないもう1つの理由は、実装getValueするキャラクターが、所属してはならないキャラクターMap(つまりProperties)から、直観的な実装Map(などProvider)とは完全に異なるユニバースまで、根本的に異なることTabularDataSupportです。

結論として、API設計のこの黄金のルールのために、とを実装していません。AbstractMapEntry#getKeygetValue

疑わしい場合は省略してください(ここを参照)

于 2008-10-25T23:26:21.283 に答える
0
  1. 理由がわからない

  2. キーと値の保存方法を実装で定義できるようにします。

于 2009-06-03T23:38:39.420 に答える