2016-10-04 17 views
26

以下の理由が説明できますか?静的な一般的な方法

public class GenericsTest<T> { 

    public void doSomething(T v1, T v2) { 

    } 

    public static <T> void doSomethingStatic(T v1, T v2) { 

    } 

    public static <T> void doSomethingStaticList(List<T> v1, List<T> v2) 
    { 

    } 

    public static void main(String[] args) { 
     GenericsTest<String> gt = new GenericsTest<>(); 

     // OK 
     gt.doSomething("abc", "abc"); 

     // Not OK 
     gt.doSomething(1, "abc"); 

     // OK 
     doSomethingStatic(1, 2); 

     // Still OK 
     doSomethingStatic(1, "abc"); 

     // So why is this not OK? 
     List<String> list1=new LinkedList<>(); 
     List<Integer> list2=new LinkedList<>(); 
     doSomethingStaticList(list1,list2); 
    } 
} 

T v1, T v2doSomethingStaticで同じ型でなければなりませんが、私はまだ、異なるタイプ(整数と文字列)を渡すことができますよ。

doSomethingStatic()がデフォルトで共通のスーパークラスを使用する場合、なぜdoSomethingStaticList()は異なるタイプで動作しないのですか?

+2

void doSomethingStatic(T v1、T v2){'、型変数は不要です。それは' public static void doSomethingStatic(Object v1、Object v2){'と意味的に同じです。 –

+0

更新:両方の型と一致する 'T'がないためです。両方のリストと一致する唯一のタイプは、 'List'の生の型ですが、有効な' List 'はありません。 – Hulk

+5

リストの場合、 'T 'はそれらを両方とも有効にするためにどのようなスーパークラス型ですか?ジェネリックは不変であることを忘れないでください。List をList に割り当てることはできません。 –

答えて

22

非スタティックの場合、GenericsTestのインスタンスを作成するときにをStringと定義します。したがって、intを渡すと、コンパイルエラーが発生します。 gt.doSomething(1, 2)を実行した場合も失敗します。

静的な場合は、Tを手動で定義しないで、パラメータから派生しています。これは両方のクラスの最初の共通スーパークラスになります。この場合はObjectです。有界ワイルドカードを使用することができます(例: <T extends Number>または<T extends CharSequence>。あなたがここに二つの異なるT秒を持っている

注:あなたが<T>を書くたび

  • GenericsTest<T>
  • public static <T> void doSomethingStatic(T v1, T v2)

ジェネリックパラメータの宣言です。この場合、混乱を避けるために異なる文字を使用することができます。

+0

静的メソッドの ''は完全なスタンドアロン/クラス自体から分離されています。したがって、Tはv1とv2の最初の一般的なスーパータイプに戻ります。この場合、これはObjectです。 ''が含まれていない場合、コンパイラはそれをクラスの 'T 'として解釈し、メソッドは静的であるため、これは不正です。 –

+0

編集はどうですか? –

+0

@ BlueRaja-DannyPflughoeft質問は、この回答を投稿してから25分後の別のシナリオを説明するために編集されました。質問のコメントに短い説明+リンクを付けました。 –

13

あなたの静的メソッドでTは彼自身の型パラメータではなく、あなたのメンバー方法で使用インスタンスためTのパラメータであるので、これは動作します。明確にするために、それの名前を変更します。

public static class GenericsTest<T> { 

    public void doSomething(T v1, T v2) { 

    } 

    public static <V> void doSomethingStatic(V v1, V v2) { 

    } 
//... 

だからdoSomething(...)の場合、ご使用のインスタンス・タイプのパラメータ値はStringあるので、それは誤りです。 typeパラメータの静的doSomethingStatic(...)値の場合は異なる。まず理論のビット

GenericsTest.doSomethingStatic(1, "abc"); //ok 
GenericsTest.<Object>doSomethingStatic(1, "abc"); //ok 
GenericsTest.<String>doSomethingStatic(1, "abc"); //not ok 
new GenericsTest<String>().doSomething(1, "abc"); //not ok 
2

  • 一般的な方法はJava Tutorials - Generic Methods
  • 型推論は、Javaで自分の型パラメータを導入する方法ですコンパイラの各メソッドの呼び出しとそれに対応する宣言を参照して、呼び出しを可能にする型引数を決定する能力Java Tutorials - Type inference

だから何が起こる:

  • を一般的な表現は、新しいジェネリック型変数を戻り値を先行したときには、「宣言」されます。したがって、クラス宣言のTは、メソッド宣言のTとは異なります(コンパイラ用)。
  • コンパイラは型推論を適用し、あなたの例では、メソッド呼び出しを適用するための適切なタイプが

あなたは、静的メソッドなしでも、この例を試すことができるオブジェクトであることを決定します。

public class GenericsTest<T> { 

    public void doSomething(T v1, T v2) { 

    } 

    public <T> void doSomething2(T v1, T v2) { 

    } 

    public static void main(String[] args) { 
    GenericsTest<String> gt = new GenericsTest<>(); 

    // ok 
    gt.doSomething("abc", "abc"); 

    // Not ok 
    gt.doSomething(1, "abc"); 

    // ok 
    gt.doSomething2(1, 2); 

    // Still ok 
    gt.doSomething2(1, "abc"); 

    } 

} 
0

静的な場合をTを手動で定義するのではなく、パラメータから派生します。 doSomethingStaticList(list1、list2)の場合、list1はString generic Listで、list2はInteger generic Listです。リストとリストは共通の型に属していないため、コンパイラの推論アルゴリズムは認識できません。

関連する問題