web-dev-qa-db-ja.com

Java Generics:戻りタイプとしてのみ定義されたジェネリックタイプ

私はGWTのGXTコードを見ていますが、Javaチュートリアルの別の例を見つけることができないジェネリックの使用に遭遇しました。クラス名は com.extjs.gxt.ui.client.data.BaseModelData すべてのコードを見たい場合。重要な部分は次のとおりです。

private RpcMap map;

public <X> X get(String property) {
  if (allowNestedValues && NestedModelUtil.isNestedProperty(property)) {
    return (X)NestedModelUtil.getNestedValue(this, property);
  }
  return map == null ? null : (X) map.get(property);
}

Xはクラスのどこでも、階層のどこでも定義されていません。Eclipseで「宣言に移動」を押すと、<X>パブリックメソッドシグネチャ。

次の2つの例を使用してこのメ​​ソッドを呼び出して、何が起こるかを確認しました。

public Date getExpiredate() {
    return  get("expiredate");
}

public String getSubject() {
    return  get("subject");
}

コンパイルされ、エラーや警告は表示されません。これを機能させるには、少なくともキャストを実行する必要があると思います。

これは、ジェネリックが任意の値をとることができ、実行時に爆発する魔法の戻り値を許可することを意味しますか?これは、ジェネリックが行うことになっていることに反するようです。誰も私にこれを説明し、おそらくこれを少し良く説明しているいくつかのドキュメントへのリンクを教えてもらえますか? Sunのジェネリックに関する23ページのpdfを調べましたが、戻り値のすべての例は、クラスレベルで定義されるか、渡されたパラメーターの1つで定義されています。

67
Jason Tholstrup

このメソッドは、予想されるすべての型を返します(_<X>_はメソッドで定義されており、完全に無制限です)。

これは、戻り値の型が実際に戻り値と一致するという規定がないため、非常に危険です。

これが持つ唯一の利点は、任意の型を返すことができるような汎用ルックアップメソッドの戻り値をキャストする必要がないことです。

私は言うでしょう:あなたはほとんどすべての型安全性を失い、get()を呼び出すたびに明示的なキャストを書く必要がないということだけを得るので、そのような構造を注意して使用してください。

そして、はい:これはほとんど実行時に爆発し、ジェネリックが何を達成すべきかという考え全体を破壊する黒魔術です。

54
Joachim Sauer

型はメソッドで宣言されます。それが「_<X>_」の意味です。タイプのスコープはメソッドに限定され、特定の呼び出しに関連しています。テストコードがコンパイルされる理由は、コンパイラが型を決定しようとし、できない場合にのみ文句を言うからです。明示する必要がある場合があります。

たとえば、 Collections.emptySet() の宣言は

_public static final <T> Set<T> emptySet()
_

この場合、コンパイラは次のことを推測できます。

_Set<String> s = Collections.emptySet();
_

ただし、できない場合は、次のように入力する必要があります。

_Collections.<String>emptySet();
_
39
sblundy

GXTクラスで同じことを理解しようとしていました。具体的には、次のシグネチャでメソッドを呼び出そうとしました:

class Model {
    public <X> X get(String property) { ... }
}

上記のメソッドをコードから呼び出してXを文字列にキャストするには、次を実行します。

public String myMethod(Data data) {
    Model model = new Model(data);
    return model.<String>get("status");
}

上記のコードはgetメソッドを呼び出し、Xによって返される型がStringとして返されることを通知します。

メソッドがあなたと同じクラスにある場合、「this。」で呼び出す必要があることがわかりました。例えば:

this.<String>get("status");

他の人が言ったように、これはGXTチームにとってかなりずさんで危険です。

5
Kyrra

BaseModelDataは安全ではないため、コンパイル時に未チェックの警告が発生します。このように使用すると、コード自体に警告はありませんが、実行時にClassCastExceptionがスローされます。

public String getExpireDate() {
  return  get("expiredate");
}
2
erickson

RpcMap(GXT API 1.2) からの興味深いメモ

getのヘッダー:

public Java.lang.Object get(Java.lang.Object key)

<X>のジェネリックパラメーターがインスタンス化されていない場合、同じ効果がありますが、「オブジェクト」をあちこちで言う必要はありません。私は他のポスターに同意します、これはずさんで少し危険です。

2
Rich

はい、これは危険です。通常、次のようにこのコードを保護します。

<X> getProperty(String name, Class<X> clazz) {
   X foo = (X) whatever(name);
   assert clazz.isAssignableFrom(foo);
   return foo;
}

String getString(String name) {
  return getProperty(name, String.class);
}

int getInt(String name) {
  return getProperty(name, Integer.class);
}
0
paulmurray