web-dev-qa-db-ja.com

ジェネリッククラスの静的メソッド?

Javaでは、次のようなものが欲しいです。

class Clazz<T> {
  static void doIt(T object) {
    // shake that booty
  }
}

しかし、私は得る

非静的型Tへの静的参照を作成できません

基本的な使用法以外にジェネリックを理解していないため、その意味を理解することはできません。主題に関する多くの情報をインターネットで見つけることができなかったのは助けにはなりません。

同様の方法で、そのような使用が可能かどうかを誰かが明確にできますか?また、最初の試みが失敗したのはなぜですか?

188
André Chalella

クラスのジェネリック型パラメーターを静的メソッドまたは静的フィールドで使用することはできません。クラスの型パラメーターは、インスタンスメソッドとインスタンスフィールドのスコープ内にのみあります。静的フィールドと静的メソッドの場合、それらはクラスのすべてのインスタンス間で共有され、異なる型パラメーターのインスタンス間でも共有されるため、明らかに特定の型パラメーターに依存することはできません。

クラスのtypeパラメータを使用する必要があるとは思われません。あなたがしようとしていることをより詳細に説明するなら、私たちはあなたがそれをするより良い方法を見つけるのを助けるかもしれません。

251
newacct

Javaは、型をインスタンス化するまで、Tが何であるかを知りません。

Clazz<T>.doit(something)を呼び出して静的メソッドを実行することもできますが、できないようです。

物事を処理する他の方法は、メソッド自体にtypeパラメーターを入れることです:

static <U> void doIt(U object)

これはUに対する適切な制限を取得しませんが、何もしないよりはましです。

133
Jason S

私はこの同じ問題に出くわしました。 JavaフレームワークでCollections.sortのソースコードをダウンロードすることで、答えを見つけました。私が使用した答えは、クラス定義ではなくメソッドに<T>ジェネリックを入れることでした。

だからこれはうまくいきました:

public class QuickSortArray  {
    public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){
//do it
}

}

もちろん、上記の答えを読んだ後、これはジェネリッククラスを使用せずに受け入れられる代替手段であることに気付きました。

public static void quickSort(Comparable[] array, int bottom, int top){
//do it
}
45
Chris

doIt()メソッドを宣言するときにジェネリックメソッドの構文を使用することにより、目的の処理を実行できます(メソッドシグネチャでstaticvoidの間に<T>が追加されていることに注意してくださいdoIt()):

class Clazz<T> {
  static <T> void doIt(T object) {
    // shake that booty
  }
}

上記のコードをCannot make a static reference to the non-static type Tエラーなしで受け入れるようにEclipseエディターを取得し、それを次の作業プログラムに拡張しました(ある程度年齢に適した文化的参照で完了します)。

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }

  private static class KC {
  }

  private static class SunshineBand {
  }

  public static void main(String args[]) {
    KC kc = new KC();
    SunshineBand sunshineBand = new SunshineBand();
    Clazz.doIt(kc);
    Clazz.doIt(sunshineBand);
  }
}

実行すると、これらの行がコンソールに出力されます:

その戦利品を振る 'クラスcom.eclipseoptions.datamanager.Clazz $ KC' !!!
その戦利品を振る「クラスcom.eclipseoptions.datamanager.Clazz $ SunshineBand」!!!

15
BD at Rivenhill

この構文はまだ言及されていないと思います(引数のないメソッドが必要な場合):

class Clazz {
  static <T> T doIt() {
    // shake that booty
  }
}

そして呼び出し:

String str = Clazz.<String>doIt();

これが誰かを助けることを願っています。

8
Oreste Viron

他の人はすでにあなたの質問に答えていますが、それに加えて、O'Reilly Java Generics の本を完全に再コメントできます。それは時々微妙で複雑な主題であり、しばしば無意味な制限があるように思えますが、本はJavaジェネリックがそうである理由を説明するかなり良い仕事をします。

4
skaffman

次のような何かがあなたを近づけます

class Clazz
{
   public static <U extends Clazz> void doIt(U thing)
   {
   }
}

編集:より詳細な例を更新

public abstract class Thingo 
{

    public static <U extends Thingo> void doIt(U p_thingo)
    {
        p_thingo.thing();
    }

    protected abstract void thing();

}

class SubThingoOne extends Thingo
{
    @Override
    protected void thing() 
    {
        System.out.println("SubThingoOne");
    }
}

class SubThingoTwo extends Thingo
{

    @Override
    protected void thing() 
    {
        System.out.println("SuThingoTwo");
    }

}

public class ThingoTest 
{

    @Test
    public void test() 
    {
        Thingo t1 = new SubThingoOne();
        Thingo t2 = new SubThingoTwo();

        Thingo.doIt(t1);
        Thingo.doIt(t2);

        // compile error -->  Thingo.doIt(new Object());
    }
}
3
ekj

クラスにジェネリック型を指定すると、JVMは定義ではなくクラスのインスタンスのみを持つことを認識します。各定義にはパラメーター化されたタイプのみがあります。

ジェネリックはC++のテンプレートのように機能するため、最初にクラスをインスタンス化してから、指定された型で関数を使用する必要があります。

2
Marcin Cylke

@BD at Rivenhill:昨年、この古い質問が再び注目を集めているので、議論のためだけに少し話を進めましょう。 doItメソッドの本体は、T固有のことは一切行いません。ここにあります:

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

したがって、すべての型変数を完全にドロップし、コーディングすることができます

public class Clazz {
  static void doIt(Object object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

OK。しかし、元の問題に戻りましょう。クラス宣言の最初の型変数は冗長です。メソッドの2番目のもののみが必要です。ここでもう一度行きますが、最終的な答えではありません。

public class Clazz  {
  static <T extends Saying> void doIt(T object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}
// Output:
// KC
// Sunshine

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

ただし、次のバージョンもまったく同じように機能するため、何も大騒ぎしません。必要なのは、メソッドパラメータのインターフェイスタイプのみです。型変数はどこにも見えません。それが本当に元の問題でしたか?

public class Clazz  {
  static void doIt(Saying object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}
1
Hubert Kauker

また、簡単に言えば、ジェネリックの「消去」プロパティのために発生します。これは、ArrayList<Integer>ArrayList<String>を定義しますが、コンパイル時には2つの異なる具体的な型のままであることを意味します。ただし、実行時にJVMはジェネリック型を消去し、2つのクラスではなく1つのArrayListクラスのみを作成します。したがって、ジェネリックの静的型メソッドまたは何かを定義すると、そのジェネリックのすべてのインスタンスで共有されます。私の例では、ArrayList<Integer>ArrayList<String>の両方で共有されます。そのため、エラーが発生しますクラスのジェネリック型パラメーターは静的コンテキストでは許可されていません!

1
user6288471

静的変数はクラスのすべてのインスタンスで共有されるため。たとえば、次のコードがある場合

class Class<T> {
  static void doIt(T object) {
    // using T here 
  }
}

Tは、インスタンスが作成された後にのみ使用可能です。ただし、インスタンスが使用可能になる前でも静的メソッドを使用できます。そのため、ジェネリック型パラメーターは静的メソッドおよび変数内で参照できません

0
Bharat