違いは何ですか ?とJavaジェネリックのオブジェクト?


137

私はEclipseを使用して、Javaジェネリックを適切に使用するためにいくつかのコードをクリーンアップしています。ほとんどの場合、それは型を推論する優れた仕事をしていますが、推論された型が可能な限り一般的でなければならない場合があります:オブジェクト。しかし、Eclipseは、オブジェクトのタイプと「?」のタイプの間で選択するオプションを私に与えているようです。

だから違いは何ですか:

HashMap<String, ?> hash1;

そして

HashMap<String, Object> hash2;

4
ワイルドカードの公式チュートリアルをご覧ください。それはそれをうまく説明し、Objectを単に使用するよりもなぜそれが必要であるかの例を与えます。
ベンS

回答:


148

インスタンスのHashMap<String, String>一致Map<String, ?>はありませんMap<String, Object>Stringsから任意のものへのマップを受け入れるメソッドを記述したいとします。

public void foobar(Map<String, Object> ms) {
    ...
}

を指定することはできませんHashMap<String, String>。あなたが書くなら

public void foobar(Map<String, ?> ms) {
    ...
}

できます!

Javaのジェネリックスで誤解されることがあるのList<String>は、のサブタイプではないということですList<Object>。(しかしString[]、実際にはのサブタイプですObject[]。これが、総称と配列がうまく混ざらない理由の1つです(Javaの配列は共変ですが、総称はそうではなく、不変です))。

サンプル:受け入れるメソッドを記述したい場合 ListInputStreamsとのサブタイプInputStreamます。

public void foobar(List<? extends InputStream> ms) {
    ...
}

ところで、Joshua Blochの効果的なJavaは、それほど単純ではないことを理解したい場合に優れたリソースです。(上記の質問についても、この本で十分に説明されています。)


1
これは、すべてのコントローラー機能のコントローラーレベルでResponseEntity <?>を使用する正しい方法ですか?
Irakli 2016年

完璧な答えヨハネス!
gaurav

36

この問題について考えるもう1つの方法は、

HashMap<String, ?> hash1;

に相当

HashMap<String, ? extends Object> hash1;

この知識をJava Generics and Collectionsのセクション(2.4)の「Get and Put Principle」と組み合わせます

取得と配置の原則:構造体から値を取得するだけの場合は拡張ワイルドカードを使用し、構造体に値を配置するだけの場合はスーパーワイルドカードを使用します。取得と配置の両方でワイルドカードを使用しないでください。

うまくいけば、ワイルドカードがもっと意味をなすようになるかもしれません。


1
「?」なら "?extends Object"はあなたをもっと混乱させるでしょう。多分。
マイケルマイヤーズ

「難しいツール」を提供して、この難しいテーマについての理由を説明できるようにすること。ワイルドカードの拡張に関する追加情報を提供しました。
Julien Chastang 2009年

2
追加情報をありがとう。まだ消化中です。:)
skiphoppy 2009年

HashMap<String, ? extends Object> だからそれnullはハッシュマップに追加されるのを防ぐだけですか?
マラウディン2017年

12

それはあなたがそれを覚えていれば理解するのは簡単ですCollection<Object>ちょうどタイプのオブジェクトを含む一般的なコレクションですObjectが、Collection<?>コレクションのすべてのタイプのスーパータイプです。


1
これは厳密には簡単ではない;-)な点がありますが、それは正しいです。
Sean Reilly

6

上記の共分散はほとんどのケースをカバーしますが、1つ欠けています:

「?」クラス階層の「オブジェクト」を含みます。StringはObjectのタイプであり、Objectは?のタイプであると言えます。すべてがオブジェクトと一致するわけではありませんが、すべてが?と一致します。

int test1(List<?> l) {
  return l.size();
}

int test2(List<Object> l) {
  return l.size();
}

List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1);  // compiles because any list will work
test1(l2);  // compiles because any list will work
test2(l1);  // fails because a ? might not be an Object
test2(l2);  // compiled because Object matches Object

4

Map<String, ?>値の型がわからないため、安全に何も入れることができません。

Map<String, Object>値はであることがわかっているため、任意のオブジェクトをに入れることができますObject


「Map <String、?>に安全に何かを入れることはできません」偽。できます。それが目的です。
ベンS

3
ベンは間違っています。タイプ<?>のコレクションに入れることができる唯一の値はnullですが、タイプ<オブジェクト>のコレクションには何でも入れることができます。
sk。

2
私の回答で与えたリンクから:「cの要素タイプが何を表すのかわからないので、それにオブジェクトを追加することはできません。」誤報をお詫びします。
ベンS

1
最大の問題は、プリミティブ以外はすべてオブジェクトであると考えると、HashMap <String、?>に何も追加できない可能性があることを理解していないことです。それともプリミティブ用ですか?
avalon

1
@avalon他の場所では、境界が定められたそのマップへの参照があります。たとえば、それはかもしれませんMap<String,Integer>Integerオブジェクトのみを値としてマップに格納する必要があります。あなたは値の型を(それはだかわからないので、しかし?)、あなたはそれが安全であればコールするかどうかを知りませんput(key, "x")put(key, 0)または何か他のもの。
エリクソン2016年

2

変数hash1として宣言できるHashMap<String, ?>ことは、変数hash1が任意HashMapのキーStringと任意のタイプの値を持つ任意のものを保持できることを示します。

HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();

変数 mapはこれらのハッシュマップのいずれかを格納できるため、上記のすべてが有効です。その変数は、それが保持するハッシュマップのValueタイプが何であるかを気にしません。

ただし、ワイルドカードを使用して、マップに任意のタイプのオブジェクトを配置することはできませ。実際のところ、上記のハッシュマップでは、map変数を使用して何も入れることができません。

map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");

上記のすべてのメソッド呼び出しは、Javaが内部のHashMapのValueタイプが何であるかを認識していないため、コンパイル時エラーになりますmap

それでもハッシュマップから値を取得できます。「値の型がわからない」(変数内のハッシュマップの種類がわからないため)が、すべてがサブクラスでObjectあり、マップから取得するものは何でもあると言えます。タイプObjectになります:

HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.

myMap.put("ABC", 10);

HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).

System.out.println(output);

上記のコードブロックは、コンソールに10を出力します。


したがって、最後に、HashMapワイルドカード付きのa を使用して、タイプが何であるかを気にしない場合(つまり、問題ではない場合)HashMapに、次に示します。

public static void printHashMapSize(Map<?, ?> anyMap) {
    // This code doesn't care what type of HashMap is inside anyMap.
    System.out.println(anyMap.size());
}

それ以外の場合は、必要なタイプを指定します。

public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
    for (int i = 'A'; i <= 'Z'; i++)
        System.out.println(anyCharacterMap.get((char) i));
}

上記のメソッドでは、マップのキーがであることを知る必要がありますCharacter。そうでなければ、マップから値を取得するために使用するタイプがわかりません。toString()ただし、すべてのオブジェクトにはメソッドがあります。そのため、マップはその値に対して任意のタイプのオブジェクトを持つことができます。それでも値を印刷できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.