it-swarm.com.de

Statische Methode in einer generischen Klasse?

In Java möchte ich etwas haben als:

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

Aber ich verstehe

Es kann kein statischer Verweis auf den nicht statischen Typ T erstellt werden

Ich verstehe Generika nicht nur über die grundlegenden Verwendungszwecke hinaus und kann daher nicht viel Sinn machen. Es hilft nicht, dass ich im Internet nicht viele Informationen zu diesem Thema finden konnte.

Könnte jemand auf eine ähnliche Weise klären, ob eine solche Verwendung möglich ist? Warum war mein ursprünglicher Versuch nicht erfolgreich?

177
André Chalella

Sie können die generischen Typparameter einer Klasse nicht in statischen Methoden oder statischen Feldern verwenden. Die Typparameter der Klasse gelten nur für Instanzmethoden und Instanzfelder. Bei statischen Feldern und statischen Methoden werden sie von allen Instanzen der Klasse gemeinsam genutzt, auch von Instanzen mit unterschiedlichen Typparametern. Sie können also offensichtlich nicht von einem bestimmten Typparameter abhängig sein.

Es scheint nicht, dass Ihr Problem die Verwendung des Typparameters der Klasse erfordert. Wenn Sie detaillierter beschreiben, was Sie zu tun versuchen, können wir Ihnen vielleicht dabei helfen, einen besseren Weg zu finden.

244
newacct

Java weiß nicht, was T ist, bis Sie einen Typ instanziieren.

Vielleicht können Sie statische Methoden ausführen, indem Sie Clazz<T>.doit(something) aufrufen, aber es klingt so, als könnten Sie es nicht.

Die andere Möglichkeit, Dinge zu behandeln, besteht darin, den Typparameter in die Methode selbst zu schreiben:

static <U> void doIt(U object)

was dir nicht die richtige Einschränkung für U gibt, aber es ist besser als nichts ....

126
Jason S

Ich bin auf das gleiche Problem gestoßen. Ich habe meine Antwort gefunden, indem ich den Quellcode für Collections.sort im Java-Framework heruntergeladen habe. Die Antwort, die ich verwendete, war, das generische <T> in die Methode einzufügen, nicht in die Klassendefinition.

Also das hat funktioniert:

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

}

Nachdem ich die obigen Antworten gelesen hatte, wurde mir klar, dass dies eine akzeptable Alternative wäre, ohne eine generische Klasse zu verwenden:

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

Sie können das tun, was Sie möchten, indem Sie die Syntax für generische Methoden verwenden, wenn Sie Ihre doIt()-Methode deklarieren (beachten Sie den Zusatz von <T> zwischen static und void in der Methodensignatur von doIt()):

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

Ich habe den Eclipse-Editor dazu gebracht, den obigen Code ohne den Cannot make a static reference to the non-static type T-Fehler zu akzeptieren, und habe ihn dann auf das folgende Arbeitsprogramm erweitert (mit etwas altersgemäßer kultureller Referenz):

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

Welche druckt diese Zeilen auf die Konsole, wenn ich sie ausführe:

schüttle diese Beute 'Klasse com.eclipseoptions.datamanager.Clazz $ KC' !!!
Schüttle diese Beute 'Klasse com.eclipseoptions.datamanager.Clazz $ SunshineBand' !!!

13
BD at Rivenhill

Ich denke, diese Syntax wurde noch nicht erwähnt (falls Sie eine Methode ohne Argumente wünschen):

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

Und der Anruf:

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

Hoffe das hilft jemandem.

6
Oreste Viron

Im Fehler wird es richtig erwähnt: Sie können keine statische Referenz auf den nicht statischen Typ T erstellen. Der Grund ist, dass der Parameter T durch ein beliebiges Argument vom Typ ersetzt werden kann, z. Clazz<String> oder Clazz<integer> usw. Statische Felder/Methoden werden jedoch von allen nicht statischen Objekten der Klasse gemeinsam genutzt. 

Der folgende Auszug ist dem doc entnommen:

Das statische Feld einer Klasse ist eine Variable auf Klassenebene, die von allen .__ gemeinsam genutzt wird. nicht statische Objekte der Klasse. Daher sind statische Felder vom Typ Parameter sind nicht erlaubt. Betrachten Sie die folgende Klasse:

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

    // ...
}

Wenn statische Felder mit Typparametern zulässig sind, wird der folgende Code verwirrt:

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

Wie lautet der tatsächliche Typ von Betriebssystem, da das statische Feld "os" von Telefon, Pager und PC gemeinsam genutzt wird? Es kann nicht Smartphone, Pager und .__ sein. TabletPC zur gleichen Zeit. Sie können daher keine statischen Felder erstellen von Typparametern.

Wie chris in seinem answer richtig hervorgehoben hat, müssen Sie mit der Methode type-Parameter und in diesem Fall nicht mit der Klasse verwenden. Sie können es schreiben wie:

static <E> void doIt(E object) 
6
i_am_zero

Andere haben Ihre Frage bereits beantwortet, aber ich kann das O'Reilly Java Generics Buch auch uneingeschränkt empfehlen. Es ist manchmal ein subtiles und komplexes Thema und scheint oft sinnlose Einschränkungen zu haben, aber das Buch erklärt ziemlich gut, warum Java-Generics so sind, wie sie sind.

4
skaffman

Etwas wie das Folgende würde dich näher bringen

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

BEARBEITEN: Aktualisiertes Beispiel mit mehr Details

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

Wenn Sie einen generischen Typ für Ihre Klasse angeben, weiß die JVM, dass sie nur über eine Instanz Ihrer Klasse verfügt, nicht über eine Definition. Jede Definition hat nur einen parametrisierten Typ.

Generics funktionieren wie Vorlagen in C++. Sie sollten daher zuerst Ihre Klasse instanziieren und dann die Funktion mit dem angegebenen Typ verwenden.

2
Marcin Cylke

@BD bei Rivenhill: Da diese alte Frage letztes Jahr wieder auf sich aufmerksam gemacht hat, lassen Sie uns ein bisschen weitergehen, nur um darüber zu diskutieren .. Der Körper Ihrer doIt-Methode hat überhaupt nichts T-spezifisch. Hier ist es:

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

Sie können also alle Typvariablen und Code einfach löschen

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

OK. Aber kommen wir noch näher an das ursprüngliche Problem heran. Die erste Typvariable in der Klassendeklaration ist redundant. Es wird nur der zweite über die Methode benötigt. Jetzt geht es wieder los, aber es ist noch nicht die endgültige Antwort: 

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

Es ist jedoch alles zu viel Aufhebens um nichts, da die folgende Version genauso funktioniert. Es wird lediglich der Schnittstellentyp im Methodenparameter benötigt. Keine Typvariablen in Sichtweite. War das wirklich das ursprüngliche Problem?

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

Vereinfacht ausgedrückt, geschieht dies aufgrund der Eigenschaft "Erasure" der Generics. Dies bedeutet, dass wir zwar ArrayList<Integer> und ArrayList<String> definieren, zum Zeitpunkt des Kompilierens jedoch zwei verschiedene konkrete Typen bleiben, die JVM jedoch zur Laufzeit generic löscht Typen und erstellt nur eine ArrayList-Klasse anstelle von zwei Klassen. Wenn wir also eine statische Typmethode oder irgendetwas für ein generisches Element definieren, wird es von allen Instanzen dieses Generikums verwendet. In meinem Beispiel wird es von ArrayList<Integer> und ArrayList<String> gemeinsam genutzt. Deshalb erhalten Sie den Fehler. Ein generischer Typparameter einer Klasse Ist in einem statischen Kontext nicht zulässig! 

1
user6288471

Da statische Variablen von allen Instanzen der Klasse gemeinsam genutzt werden. Zum Beispiel, wenn Sie folgenden Code haben

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

T ist nur verfügbar, nachdem eine Instanz erstellt wurde. Statische Methoden können jedoch bereits verwendet werden, bevor Instanzen verfügbar sind. Daher können generische Typparameter nicht innerhalb statischer Methoden und Variablen referenziert werden

0
Bharat