ジェネリックメソッドのJava境界パラメータ

私はジェネリックメソッドで束縛されたパラメータでいくつかのことをテストしました。私はいくつかの奇妙な動作を発見しました。
誰かが次のコードスニペットで2つのエラーを説明できるなら、素晴らしいことでしょう。

2つのクラス Class1 Class2 の両方が BaseClass から拡張されているとします。 Class2 はInterfaceを実装しています。

Class1 には、次のように Class2 のインスタンスを返すメソッドがあります。

public class Class2 extends BaseClass implements Interface {

    @Override
    public void method() {
        System.out.println("test"); //$NON-NLS-1$
    }
}

public class Class1 extends BaseClass {

    public  T getTwo() {
        return new Class2();
       //Error: Type mismatch: cannot convert from Class2 to T
    }

    public static void main(String[] args) {
        Interface two = new Class1().getTwo();
       //Error: Bound mismatch: The generic method getTwo() of type Class1 is
       //not applicable for the arguments (). The inferred type Interface is
       //not a valid substitute for the bounded parameter 
        System.out.println(two);
    }
}
10
それはジェネリック医薬品の悪用のようです。実際には、 Class2 BaseClass 、または Interface のいずれかの戻り値の型を getTwo に設定する必要があります。 1つのメソッドに対してのみ T が定義されている場合、コンパイラは、 new Class1()。getTwo() getTwo がこのタイプの入力パラメータを持つ場合にのみ可能です。
追加された 著者 DRCB,

2 答え

メソッドによって宣言された型パラメーターは、メソッドの実装ではなく呼び出し元によって指定されるため、最初のコンパイル・エラーが発生します。つまり、与えられた

class Class3 extends BaseClass implements Interface { ... }

発信者が

Class3 c3 = new Class1().getTwo();

メソッドの実装は、 T = Class3 のサブタイプではない Class2 を返します。

2番目のコンパイルエラーは、呼び出し元によって明示的に指定されていない型パラメーターが、メソッドの引き数と、メソッドの戻り値が割り当てられる変数の型から推測されるために発生します。この推論はここで失敗する。 Java言語仕様で推奨されている通常の回避策は、そのような場合に型パラメータを明示的に指定することです(型推論は、単純な場合の便宜を意図していますが、すべてのケースをカバーすることを目的としていません)。

この型パラメータを正しく宣言する方法については、これらの宣言で何を達成しようとしているのかを知る必要があります。

5
追加された
はいもちろん。ありがとう、固定。
追加された 著者 meriton,
さて、 Class3 の例と同様に、型パラメータを明示的に指定しようとしましたか? Class2 two = new Class1()。getTwo(); を宣言してタイプ推論を調整しますか?なぜあなたのシステムで違った振る舞いをするのかを説明することはできません。あなたは同じ設定で同じコンパイラを使ってまったく同じサンプルプログラムを試しましたか?
追加された 著者 meriton,
私はそれを想像しています。おそらく抽象クラスBaseClassWithInterfaceを定義し、BaseClassはInterface {} を実装し、それを拡張するために Class2 を必要とします。呼び出し元は、 BaseClassWithInterface の変数を使用してオブジェクトを格納できます。なぜあなたの質問について:おそらく、セクション Java言語仕様の15.12.2.7 にそう言われています。
追加された 著者 meriton,
+1良い答え。私はシンタックスはあなたの2番目のコード例では new Class1()。 getTwo()と思う。
追加された 著者 Paul Bellora,
私は最初のコンパイルエラーの点を見ていますが、私は2番目のコンパイルエラーについては確信しています。私のシステムではOKのようですが、同僚のシステムでは失敗し、 Interface two = new Class1()。getTwo()コールは BaseClass two = new Class1()。getTwo(); ); も私のシステムでは失敗します(上記のように)...
追加された 著者 Marco,
"getwo()" - MethodがInterfaceの一部であり、getTwoの正確な戻り値の型がわからない場合、返されたオブジェクトを汎用的な方法で使いたいとします。なぜあなたは 'BaseClass two = class1.getTwo();'を知っていますか? OKであり、 'Interface two = class1.getTwo();'コンパイルできないのですか?
追加された 著者 Marco,
ああ、これは型推論が正しく機能する方法です。説明したように BaseClassWithInterface を定義するか、汎用パラメータでインタフェースを定義することができます。私はjavaのジェネリックスについてのより深い洞察を得たと思う。ありがとうマルコ
追加された 著者 Marco,

Class2 知っているときに、 getTwo メソッドのジェネリックを使う理由は?単にこれを行う:

public Class2 getTwo() {
    return new Class2();
}

If you're overriding a method public T getTwo(), the compiler will allow you to declare your impl as public Class2 getTwo() when your T for your impl is Class2

4
追加された
そのオーバーライドであまり速くない。あなたのシナリオでは、OPのコードが許可されなかったのと同じ理由でClass2を返すもので制約を満たすTを返すメソッドをオーバーライドします。私は、Javaが実在する量限定子で戻り値の型を表現することさえできるのかどうかはわかりません。
追加された 著者 Judge Mental,