web-dev-qa-db-ja.com

ImmutableMapまたはMapを返す方が良いですか?

Map を返すメソッドを書いているとしましょう。例えば:

public Map<String, Integer> foo() {
  return new HashMap<String, Integer>();
}

しばらくそれについて考えた後、このマップが作成された後に変更する理由はないと判断しました。したがって、 ImmutableMap を返します。

public Map<String, Integer> foo() {
  return ImmutableMap.of();
}

戻り値の型を汎用のMapのままにしておくべきですか、それともImmutableMapを返すように指定する必要がありますか?

一方から、これがまさにインターフェースが作成された理由です。実装の詳細を非表示にします。
一方、このままにしておくと、他の開発者はこのオブジェクトが不変であるという事実を見逃す可能性があります。したがって、不変オブジェクトの主要な目標を達成することはできません。変更できるオブジェクトの数を最小限に抑えることで、コードをより明確にします。最悪の場合、しばらくすると、誰かがこのオブジェクトを変更しようとする可能性があり、これによりランタイムエラーが発生します(コンパイラは警告しません)。

88
AMT
  • パブリックAPIを記述していて、その不変性が設計の重要な側面である場合、返されるマップが不変であることをメソッドの名前に明示的に示すか、具体的なタイプの地図。私の意見では、javadocで言及するだけでは十分ではありません。

    あなたは明らかにグアバの実装を使用しているので、私はドキュメントを見て、それは抽象クラスなので、実際の具体的な型に少し柔軟性を与えます。

  • 内部ツール/ライブラリを作成している場合は、単純なMapを返すだけで十分です。人々は、呼び出しているコードの内部について知っているか、少なくとも簡単にアクセスできます。

私の結論は、明示的は良いということで、物事を偶然に任せないでください。

68
Dici

戻り型としてImmutableMapが必要です。 Mapには、ImmutableMapの実装でサポートされていないメソッド(例:put)が含まれ、ImmutableMap@deprecatedとマークされています。

非推奨のメソッドを使用すると、コンパイラの警告が発生し、ほとんどのIDEは、非推奨のメソッドを使用しようとすると警告を表示します。

この高度な警告は、何かが間違っているという最初のヒントとしてランタイム例外を使用するよりも望ましい方法です。

36
Synesso

一方、このままにすると、他の開発者はこのオブジェクトが不変であるという事実を見逃す可能性があります。

あなたはjavadocsでそれを言及する必要があります。開発者はそれらを読んでいますよね。

したがって、不変オブジェクトの主要な目標を達成することはできません。変更できるオブジェクトの数を最小限に抑えることで、コードをより明確にします。最悪の場合、しばらくすると、誰かがこのオブジェクトを変更しようとする可能性があり、これによりランタイムエラーが発生します(コンパイラは警告しません)。

開発者はコードをテストせずに公開しません。そして、テストを行うと、例外だけでなく、理由だけでなく、不変のマップに書き込もうとしたファイルと行も表示されます。

ただし、Map自体のみが不変であり、含まれるオブジェクトは不変ではありません。

16
tkausl

このままにしておくと、他の開発者はこのオブジェクトが不変であるという事実を見逃す可能性があります

それは事実ですが、他の開発者はコードをテストし、カバーされていることを確認する必要があります。

それにもかかわらず、これを解決するためにさらに2つのオプションがあります。

  • Javadocを使用

    @return a immutable map
    
  • 説明的なメソッド名を選択

    public Map<String, Integer> getImmutableMap()
    public Map<String, Integer> getUnmodifiableEntries()
    

    具体的なユースケースの場合は、メソッドにさらに名前を付けることもできます。例えば。

    public Map<String, Integer> getUnmodifiableCountByWords()
    

他に何ができますか?!

あなたが返すことができます

  • コピー

    private Map<String, Integer> myMap;
    
    public Map<String, Integer> foo() {
      return new HashMap<String, Integer>(myMap);
    }
    

    多くのクライアントがマップを変更することが予想される場合、およびマップに少数のエントリのみが含まれている限り、このアプローチを使用する必要があります。

  • CopyOnWriteMap

    コピーオンライトコレクションは通常、対処する必要があるときに使用されます
    並行性。ただし、この概念は、CopyOnWriteMapが変更操作(追加、削除など)で内部データ構造のコピーを作成するため、状況にも役立ちます。

    この場合、マップを囲む薄いラッパーが必要になります。このラッパーは、すべてのメソッド呼び出しを、基礎となるマップに委任します。ただし、変更操作は除きます。変異操作が呼び出されると、基になるマップのコピーが作成され、以降のすべての呼び出しはこのコピーに委任されます。

    一部のクライアントがマップを変更することが予想される場合、このアプローチを使用する必要があります。

    悲しいことに、JavaにはそのようなCopyOnWriteMapがありません。ただし、サードパーティを見つけるか、自分で実装することがあります。

最後に、マップ内の要素はまだ変更可能である可能性があることに注意してください。

10
René Link

正当な理由であるImmutableMapを確実に返します。

  • メソッドの署名(戻り値の型を含む)は自己文書化する必要があります。コメントは顧客サービスのようなものです。クライアントがそれらに依存する必要がある場合、主な製品に欠陥があります。
  • 何かがインターフェースまたはクラスであるかどうかは、それを拡張または実装する場合にのみ関係します。インスタンス(オブジェクト)が与えられると、99%の時間、クライアントコードは、何かがインターフェースなのかクラスなのかを知りません。最初は、ImmutableMapはインターフェースであると想定していました。リンクをクリックして初めて、クラスであることに気付きました。
8
Benito Ciaro

クラス自体に依存します。グアバのImmutableMapは、可変クラスへの不変のビューを意図したものではありません。クラスが不変であり、基本的にImmutableMapである構造を持つ場合、戻り値の型をImmutableMapにします。ただし、クラスが変更可能な場合は、変更しないでください。これがある場合:

public ImmutableMap<String, Integer> foo() {
    return ImmutableMap.copyOf(internalMap);
}

グアバは毎回地図をコピーします。遅いです。ただし、internalMapがすでにImmutableMapであった場合、まったく問題ありません。

クラスをImmutableMapを返すように制限しない場合、代わりにCollections.unmodifiableMapを返すことができます。

public Map<String, Integer> foo() {
    return Collections.unmodifiableMap(internalMap);
}

これはマップへの不変のviewであることに注意してください。 internalMapが変更されると、Collections.unmodifiableMap(internalMap)のキャッシュコピーも変更されます。しかし、私はまだゲッターにそれを好む。

5
Justin

これは正確な質問に答えているわけではありませんが、マップをまったく返すべきかどうかを検討する価値があります。マップが不変の場合、提供される主要なメソッドはget(key)に基づきます。

public Integer fooOf(String key) {
    return map.get(key);
}

これにより、APIがより緊密になります。実際にマップが必要な場合、エントリのストリームを提供することにより、これをAPIのクライアントに任せることができます。

public Stream<Map.Entry<String, Integer>> foos() {
    map.entrySet().stream()
}

その後、クライアントは必要に応じて独自の不変または可変マップを作成するか、独自のマップにエントリを追加できます。クライアントが値が存在するかどうかを知る必要がある場合、代わりにoptionalを返すことができます:

public Optional<Integer> fooOf(String key) {
    return Optional.ofNullable(map.get(key));
}
4
Solubris