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


197

Javaでは、次のようなものにしたいと思います:

class Clazz<T> {
  static void doIt(T object) {
    // ...
  }
}

しかし、私は得る

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

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

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

回答:


270

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

あなたの問題がクラスの型パラメーターを使用する必要があるべきではないようです。あなたがやろうとしていることをより詳細に説明するなら、多分私たちはあなたがそれを行うためのより良い方法を見つけるのを助けることができます。


9
賛成されたこの回答は、単に回避策を提供するのではなく、実際に投稿者の問題を説明しています。
ジョーン

7
@Andre:あなたの直感は根拠のないものではありません。C#は確かにジェネリックをこのように扱います。
jyoungdev 2010年

32
「静的フィールドと静的メソッドの場合、それらはクラスのすべてのインスタンス間で共有され、異なる型パラメーターのインスタンスも共有されます...」痛い!型消しでナッツをもう一度キック!
BDがRivenhillで11

2
コンパイル後にジェネリッククラス/メソッドがどのように見えるかを確認すると、ジェネリック属性が削除されていることがわかります。また、コンパイル後のList <Integer>は「List」のようになります。したがって、コンパイル後のList <Integer>とList <Long>に違いはありません。どちらもListになりました。
Dainius、2012年

3
彼はかなりうまくやろうとしていることを説明したと思います。彼がお尻を揺さぶろうとしているのは明らかです!hah
astryk 2015

140

JavaはT、型をインスタンス化するまでは何であるかを認識しません。

呼び出しによって静的メソッドを実行できるかもしれませんがClazz<T>.doit(something)、それはできないようです。

処理するもう1つの方法は、メソッド自体に型パラメーターを配置することです。

static <U> void doIt(U object)

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


次に、制約を指定せずにメソッドを呼び出します。つまり、Clazz <Object> .doIt(object)ではなくClazz.doIt(object)ですよね?それは大丈夫だと思いますか?
アンドレ・Chalella

2番目の構文にはより正確な構文があり、メソッドが呼び出されたcontaxtからの戻り値の型をコンパイラーが推測できない場合に必要です。つまり、コンパイラがClazz.doIt(object)を許可している場合は、それを実行します。
スカフマン、2009年

1
Clazz <Object> .doIt(object)を試しましたが、コンパイル時エラーが発生しました!「トークンの構文エラー、配置ミスの構成」。Clazz.doIt(object)は正常に機能しますが、警告も表示されません。
アンドレ・Chalella

8
Clazz。<Object> doIt(object)を使用します。C ++のClazz <int> :: doIt(1)と比較すると、奇妙な構文だと思います
Crend King

Uに対する適切な制限が適用されないのはなぜですか。
Adam Burley

46

私はこれと同じ問題に遭遇しました。Collections.sortJavaフレームワークのソースコードをダウンロードすることで私の答えを見つけました。私が使用した答えは、<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
}

8
受け入れられた答えは技術的に正しいものでしたが、これは実際にGoogleがこの質問に私を導いたときに私が探していたものです。
ジェレミーリスト

T extends XXX構文を含む例を提供するための小道具。
Ogre Psalm33 2014年

3
@Chrisとにかくジェネリックを使用しているので、ジェネリックをすべて使用することもできますComparable。つまり、のような生の型を使用しないことです。<T extends Comparable<? super T>>代わりに試してください。
easoncxz 2015

15

メソッドを宣言するときにジェネリックメソッドの構文を使用することで、必要なことを実行できますdoIt()(とのメソッドシグネチャの<T>間に追加されていることに注意してください)。staticvoiddoIt()

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

私はEclipseエディターに上記のコードをCannot make a static reference to the non-static type Tエラーなしで受け入れさせ、それを次の作業用プログラムに拡張しました(ある程度年齢に応じた文化的参照を備えた完全なものです)。

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」を振ってください!!!


この場合、2番目の<T>は最初のものを非表示にしますか?
アンドレシャレーラ

1
@AndréNeves、はい。2 <T>つ目はclass C { int x; C(int x) { ... } }、パラメータxでフィールドをマスクするのと同じ方法で、1つ目をマスクしますx
マイクサミュエル

サンプルの出力を説明する方法:実際に2つのタイプが関与しているようですが、1つはパラメーターT値によるものですか?Tがクラスタイプを本当に非表示にしていた場合、パラメーターなしで「shake that booty 'class com.eclipseoptions.datamanager.Clazz」が2回出力されることを期待します。
Pragmateek

1
マスキングとは関係ありません。静的メソッドは、クラスのジェネリック型によってバインドすることはできません。それでも、独自のジェネリック型と境界を定義できます。
マイク2013

静的型を一度定義して、いくつかの静的メソッドで再利用する方法はありますか?私はやってのファンではないstatic <E extends SomeClass> E foo(E input){return input;}私のような何かをしたいとき、それぞれ、すべての方法のためにstatic <E extends SomeClass>; //static generic type defined only once and reused static E foo(E input){return input;} static E bar(E input){return input;} //...etc...
HesNotTheStig

14

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

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

そして呼び出し:

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

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


1
実際には(Javaのバージョンはわかりません)、<String>変数に割り当てることで型引数を単に推論するだけなので、これがなくても可能です。割り当てない場合は、単に推測されObjectます。
Adowrath 2017年

これは完璧なソリューションです。
Prabhat Ranjan

6

それは、正しくエラーに記載されている:あなたは非静的型Tへの静的な参照を作ることができない理由は型パラメータがされたT型引数の例のいずれかで置き換えることができるClazz<String>か、Clazz<integer>などしかし、staticフィールド/メソッドは、すべての非によって共有されています-クラスの静的オブジェクト。

次の抜粋は、ドキュメントから抜粋したものです。

クラスの静的フィールドは、クラスのすべての非静的オブジェクトによって共有されるクラスレベルの変数です。したがって、型パラメーターの静的フィールドは許可されません。次のクラスを考えてみましょう:

public class MobileDevice<T> {
    private static T os;

    // ...
}

タイプパラメータの静的フィールドが許可されている場合、次のコードは混乱します。

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

静的フィールドosは電話、ポケットベル、およびpcで共有されるため、osの実際のタイプは何ですか?Smartphone、Pager、TabletPCを同時に使用することはできません。したがって、型パラメーターの静的フィールドを作成することはできません。

クリスが彼の回答で正しく指摘したように、この場合はクラスではなくメソッドで型パラメーターを使用する必要があります。あなたはそれを次のように書くことができます:

static <E> void doIt(E object) 

3

次のようなものが近づきます

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());
    }
}

まさにジェイソンSが示唆したとおり。
アンドレシャレーラ

@Andre以前の提案は、Uが
Clazzを

わかりましたが、制約以外は何も役に立ちません。私の元の投稿では、パラメーターとしてUを渡さずにこれを実行できるようにしたいと考えています。
アンドレシャレーラ

@Andre上記の私の更新を参照してください。ジェネリック型はメソッド定義でのみ定義されており、メソッドを呼び出すときに渡す必要はありません
ekj

@ekjありがとうございます、わかりました。申し訳ありませんが、この質問は3年前にJavaを毎日行っていたときに提起しました。これは私が当初意図したものではないことを理解していると思いますが、あなたの考えがわかりました。しかし、私はあなたの答えが好きでした。
アンドレ・Chalella

2

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

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


2
JavaジェネリックはC ++テンプレートとはかなり異なります。ジェネリッククラスはそれ自体でコンパイルされます。実際にはC ++での同等のコードは、(次のようになり、コードを呼び出す動作しますClazz<int>::doIt( 5 )
dribeas -デビッド・ロドリゲス

私はこの答えが受け入れられたものよりも好きです... C ++テンプレート参照を除いて、ジェネリック型はクラス全体ではなくクラスの1つのインスタンスのみにあるというコメントがスポットされています。受け入れられた答えはこれを説明せず、静的メソッドでクラスのジェネリック型を使用できない理由とは何の関係もない回避策を提供するだけです。
ジョーン

1

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


1

@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";
      }
}

0

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.