23

Collections.unmodifiableList(...)静的内部クラス の新しいインスタンスを返しますUnmodifiableList。他の変更不可能なコレクション クラスは、同じ方法で構築されます。

これらのクラスが公開された場合、1 つには 2 つの利点がありました

  • より具体的な戻り値 ( などUnmodifiableList) を示す機能。これにより、API ユーザーはそのコレクションを変更するという考えに至りません。
  • Listaが であるか実行時にチェックする機能instanceof UnmodifiableList

では、これらのクラスを公開しないことに利点はありましたか?

編集:確実に説得力のある議論が提示されなかったため、最も支持された回答を選択します。

4

7 に答える 7

9

個人的にはあなたに完全に同意します。問題の中心にあるのは、Java のジェネリックが共変ではないという事実です。これは、Java のコレクションが変更可能であるためです。

Java の型システムでは、 mutators実際には immutableであると思われる型を成文化することはできません。ソリューションの設計を開始する場合を想像してください。

interface Immutable //marker for immutability

interface ImmutableMap<K, V> extends Map<K, V>, Immutable

しかし、ImmutableMapは のサブクラスであるMapため、そのような不変の Map を返す任意のメソッドMapから割り当てることができます。ImmutableMap

public ImmutableMap<K, V> foo();

Map に割り当てることができるため、コンパイル時に変更できます。

Map<K, V> m = foo();
m.put(k, v); //oh dear

したがって、この型を追加しても、実際には何も悪いことを防げていないことがわかります。そのため、十分に提供できないという判断が下されたと思います。


scalaのような言語には、宣言サイトの差異に関する注釈があります。つまり、Scala の Map のように型を共変 (したがって不変) として指定することができます (実際にはそのVパラメーターで共変です)。したがって、API は戻り値の型が可変か不変かを宣言できます。

余談ですが、Scala では交差タイプを宣言できるため、ImmutableXYZインターフェイスを別のエンティティとして作成する必要さえありません。返すメソッドを指定できます。

def foo : XYZ with Immutable

しかし、scala には適切な型システムがありますが、Java にはありません。

于 2010-11-28T19:48:20.803 に答える
5

両方の利点があると思いますが、それほど役に立ちません。主な問題は同じままです。UnmodifiableList は引き続き List であるため、すべてのセッターが使用可能であり、基になるコレクションは引き続き変更可能です。クラス UnmodifiableList を公開すると、変更不可能であるという錯覚が追加されます。

より良い方法は、コンパイラが支援することですが、そのためには、コレクション クラスの階層を大幅に変更する必要があります。たとえば、Scala のコレクション API は、その点でより高度です。

不利な点は、少なくとも 3 つの追加のクラス/インターフェースを API に導入することです。それらはそれほど有用ではないため、API から除外することは良い選択だと思います。

于 2010-11-28T17:58:54.320 に答える
3

リストが Collections.unmodifiableList で作成されたかどうかを確認することが重要な場合は、インスタンスを作成してクラスを要求できます。これで、このクラスを任意のリストのクラスと比較できます。

private static Class UNMODIFIABLE_LIST_CLASS = 
    Collections.unmodifiableList( new ArrayList() ).getClass();
...
if( UNMODIFIABLE_LIST_CLASS == listToTest.getClass() ){
    ...
} 
于 2010-11-28T19:29:13.580 に答える
3

その理由に対する答えは非常に単純です。当時、1998 年には、効率的な設計は少しばかげていました。人々はそれについて考えましたが、それは明らかに優先事項ではありませんでした。しかし、それについての真の、深い思考はありませんでした。

このような仕組みを使いたい場合は、Guava の ImmutableList/Set/Map/... を使用してください。

それらは明示的に不変であり、そのライブラリを使用する場合の良い習慣は、たとえばリストではなく ImmutableList を返すことです。したがって、List/Set/Map/... は不変であることがわかります。

例:

private final ImmutableList constants = ...;
public final ImmutableList<String> getConstants() {
  return constants;
}

UnmodifiableXxx の設計自体については、次のようにすることもできます。

public static final class UnmodifiableXxx implements Xxx { // don't allow extend
  // static if inside Collections
  UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation
    ...
  }
  ...
}
于 2011-01-03T10:14:05.953 に答える
1

パブリッククラスUnmodifiableList だったとしましょう。プログラマーを誤った安心感に陥らせるのではないかと思います。覚えておいてください、UnmodifiableList変更可能なのビューです。これは、基になるに加えられた変更によって、の内容が引き続き変更される可能性があることを意味します。ナイーブなプログラマーは、このニュアンスを理解していない可能性があり、インスタンスが不変であると期待する可能性があります。 ListUnmodifiableListListUnmodifiableList

于 2010-11-28T12:22:58.300 に答える
1
  • より具体的な戻り値 ( などUnmodifiableList) を示す機能。これにより、API ユーザーはそのコレクションを変更するという考えに至りません。

適切な API では、これは変更不可能なリストを返すメソッドの javadoc にすでに記載されているはずです。

  • Listaが であるか実行時にチェックする機能instanceof UnmodifiableList

このような必要性は、実際の問題が別の場所にあることを示しています。コード設計の欠陥です。Listaが or のインスタンスであるかどうかを確認する必要があったことはありますArrayListLinkedList? それが であるか、ArrayListコードの実行時ではなく、コードの書き込み時に行われる決定であることは明らかです。を変更しようとしているために問題が発生した場合(API 開発者には、既に文書化されているはずの非常に適切な理由がある可能性があります)、それは実行時の問題ではなく、むしろあなた自身のせいです。LinkedListUnmodifiableListUnmodifiableList

結局のところ、それは意味がありません。、およびdo はCollections#unmodifiableXXX()、具体的な実装を表すものではありません。それらはすべて、基礎となる具体的な実装に関係なく適用できる単なるデコレータです。synchronizedXXX()checkedXXX()

于 2010-11-28T19:57:32.697 に答える
0

答えは、メソッドフォームが使用されるジェネリックを適切に認識し、この情報を渡すために追加のプログラミングを必要としないためだと思いますが、クラスフォームはもっといじる必要があります。のメソッド形式にunmodifiableMapは 2 つのフローティング ジェネリック引数があり、戻り値の型と渡された引数の両方のジェネリック引数にマップされます。

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
    return new UnmodifiableMap<K,V>(m);
}
于 2010-11-28T18:02:33.090 に答える