30

Java でジェネリック クラスを作成する場合 (クラスにはジェネリック型パラメーターがあります)、ジェネリック メソッドを使用できますか (メソッドはジェネリック型パラメーターを取ります)。

次の例を検討してください。

public class MyClass {
  public <K> K doSomething(K k){
    return k;
  }
}

public class MyGenericClass<T> {
  public <K> K doSomething(K k){
    return k;
  }

  public <K> List<K> makeSingletonList(K k){
    return Collections.singletonList(k);
  }
}

ジェネリック メソッドで期待されるように、任意のオブジェクトdoSomething(K)で のインスタンスを呼び出すことができます。MyClass

MyClass clazz = new MyClass();
String string = clazz.doSomething("String");
Integer integer = clazz.doSomething(1);

MyGenericClass ただし、ジェネリック型を指定せずにのインスタンスを使用しようとすると、渡されたものに関係なく、 を呼び出すとdoSomething(K)が返されます。ObjectK

MyGenericClass untyped = new MyGenericClass();
// this doesn't compile - "Incompatible types. Required: String, Found: Object"
String string = untyped.doSomething("String");

奇妙なことに、戻り値の型がジェネリック クラスの場合はコンパイルされますList<K>(実際には、これは説明できます - 以下の回答を参照してください)。

MyGenericClass untyped = new MyGenericClass();
List<String> list = untyped.makeSingletonList("String"); // this compiles

また、ワイルドカードのみであっても、ジェネリック クラスが型指定されている場合はコンパイルされます。

MyGenericClass<?> wildcard = new MyGenericClass();
String string = wildcard.doSomething("String"); // this compiles
  • 型指定されていないジェネリック クラスでジェネリック メソッドを呼び出すことが機能しない正当な理由はありますか?

  • 私が見逃しているジェネリッククラスとジェネリックメソッドに関連する巧妙なトリックはありますか?

編集:

明確にするために、型指定されていない、または生の型指定されたジェネリック クラスは、ジェネリック クラスの型パラメーターを尊重しないと予想します (それらが提供されていないため)。ただし、型指定されていない、または型指定されていないジェネリック クラスが、ジェネリック メソッドが尊重されないことを意味する理由は明らかではありません。

この問題は SO ですでに提起されていることが明らかになりました。この質問を参照してください。これに対する答えは、クラスが型付けされていない/生の形式の場合、ジェネリックメソッドの型付けを含め、すべてのジェネリックがクラスから削除されることを説明しています。

しかし、なぜそうなのかについては、まったく説明がありません。それでは、私の質問を明確にさせてください:

  • Java が型指定されていない、または raw 型のジェネリック クラスでのジェネリック メソッドの型付けを削除するのはなぜですか? これには正当な理由がありますか、それとも単なる見落としでしたか?

編集 - JLS の議論:

これはJLS 4.8で扱われることが (前の SO の質問とこの質問への回答で) 提案されており、次のように述べられています。

コンストラクター (§8.8)、インスタンス メソッド (§8.4、§9.4)、または非静的フィールド (§8.3) の型 M スーパークラスまたはスーパーインターフェイスから継承されていない生の型 C は、対応する生の型です。 C に対応するジェネリック宣言での型の消去に。

これが型指定されていないクラスにどのように関連するかは明らかです。クラスのジェネリック型は消去型に置き換えられます。クラスジェネリックがバインドされている場合、消去タイプはそれらの境界に対応します。それらがバインドされていない場合、消去タイプはオブジェクトです-例

// unbound class types
public class MyGenericClass<T> {
  public T doSomething(T t) { return t; }
}
MyGenericClass untyped = new MyGenericClass();
Object t = untyped.doSomething("String");

// bound class types
public class MyBoundedGenericClass<T extends Number> {
  public T doSomething(T t) { return t; }
}
MyBoundedGenericClass bounded = new MyBoundedGenericClass();
Object t1 = bounded.doSomething("String"); // does not compile
Number t2 = bounded.doSomething(1); // does compile

ジェネリック メソッドはインスタンス メソッドですが、JLS 4.8 がジェネリック メソッドに適用されるかどうかはわかりません。ジェネリック メソッドの型 (<K>前の例) は型指定されていません。その型はメソッド パラメーターによって決定されるためです。

4

6 に答える 6

4

Java Generics では、生の形式のジェネリック クラスを使用すると、そのクラスのすべてのジェネリックが生にmakeSingletonListなりdoSomethingます。私が理解している理由は、ジェネリック以前に記述された Java コードとの下位互換性を提供するためです。

ジェネリック型パラメータが不要な場合はT、単純に から削除してMyGenericClass、メソッドをジェネリックのままにしますKTそれ以外の場合は、クラス内の他のものでジェネリックを使用するには、クラス型パラメーターをクラスに指定する必要があるという事実に対処する必要があります。

于 2013-08-01T18:20:43.687 に答える
2

ジェネリックを完全に破棄する 1 つの理由を見つけました (ただし、あまり良くありません)。理由は次のとおりです。ジェネリックは制限される可能性があります。このクラスを考えてみましょう:

public static class MyGenericClass<T> {
    public <K extends T> K doSomething(K k){
        return k;
    }

    public <K> List<K> makeSingletonList(K k){
        return Collections.singletonList(k);
    }
}

doSomethingジェネリックなしでクラスを使用する場合、コンパイラは のジェネリックを破棄する必要があります。そして、この動作と一貫性を保つために、すべてのジェネリックが破棄されていると思います。

makeSingletonListJava が未チェックのキャストを行うため、コンパイルListList<K>れます (ただし、コンパイラは警告を表示します)。

于 2013-08-06T11:48:42.117 に答える
0

この理由は、ジェネリック以前のコードとの後方互換性のためです。ジェネリック以前のコードはジェネリック引数を使用せず、代わりに現在生の型と思われるものを使用していました。ジェネリック前のコードはObject、ジェネリック型を使用する参照の代わりに参照を使用し、生の型Objectはすべてのジェネリック引数に型を使用するため、コードは確かに下位互換性がありました。

例として、次のコードを検討してください。

List list = new ArrayList();

これはジェネリック前のコードであり、ジェネリックが導入されると、これは次のものと同等の生のジェネリック型として解釈されました。

List<?> list = new ArrayList<>();

なぜなら ?extendsの後にorキーワードがないsuper場合、次のように変換されます。

List<Object> list = new ArrayList<>();

のバージョンはList、ジェネリックが を使用Objectしてリスト要素を参照する前に使用されていました。このバージョンも を使用しObjectてリスト要素を参照するため、下位互換性が保持されます。

于 2013-08-06T10:30:12.230 に答える