Wie mache ich eine Java Generic-Methode statisch?

172

Das Folgende ist ein Ausschnitt darüber, wie eine generische Java-Klasse erstellt wird, um ein einzelnes Element an ein Array anzuhängen. Wie kann ich appendToArray zu einer statischen Methode machen? Das Hinzufügen von Statik zur Methodensignatur führt zu Kompilierungsfehlern.

public class ArrayUtils<E> {

        public E[] appendToArray(E[] array, E item) {
            E[] result = (E[])new Object[array.length+1];
            result[array.length] = item;
            return result;
        }
}
Chris Johnson
quelle
Welche Kompilierungsfehler erhalten Sie? Warum nicht einfach einen der Standard-Bibliothekscontainer verwenden?
Karl Knechtel
1
Kompilierungsfehler: Ich habe den statischen Modifikator tatsächlich falsch hinzugefügt. Verwenden von Sammlungen: Ja, die Verwendung einer Sammlung wäre ideal, aber die Frage bezieht sich nicht auf Sammlungen im Vergleich zu Arrays. Mein Anwendungsfall erfordert ein Array.
Chris Johnson
Beachten Sie, dass Sie die (EVIL) -Reflexion verwenden müssen, um zu verhindern, dass Clientcode unter bestimmten, aber nicht allen Umständen eine Ausnahme auslöst (nett). Vermeiden Sie am besten Referenz-Arrays.
Tom Hawtin - Tackline

Antworten:

283

Sie können nur Ihre Signatur in ändern

public static <E> E[] appendToArray(E[] array, E item)

Wichtige Details:

Generische Ausdrücke vor dem Rückgabewert führen immer eine neue generische Typvariable ein (deklarieren sie).

Außerdem stören sich Typvariablen zwischen types ( ArrayUtils) und statischen Methoden ( appendToArray) nie gegenseitig.

Also, was bedeutet das: In meiner Antwort <E>würde verstecken die Eaus , ArrayUtils<E>wenn das Verfahren nicht wäre static. UND <E>hat nichts mit dem Evon zu tun ArrayUtils<E>.

Um diese Tatsache besser widerzuspiegeln, wäre eine korrektere Antwort:

public static <I> I[] appendToArray(I[] array, I item)
scheffield
quelle
30
Bitte beachten Sie auch, dass es absolut keine Beziehung zwischen der Typvariablen auf Klassenebene Eund der statischen Methodentypvariablen gibt E. Ich halte es für viel besser, einen anderen Variablennamen zu verwenden, wenn generische Methoden statisch oder anderweitig in generischen Klassen deklariert werden.
Richter Mental
In diesem Fall kann ich jedoch ein Objekt unterschiedlichen Typs an die Parameter übergeben. Als ob ich das Integer [] Array als ersten Parameter und Double Item übergeben könnte.
Pinkpanther
pinkpanther: Stimmt, aber es schadet nicht, da die statische Methode immer nur ein Array-Objekt bearbeitet, das über einen Parameter an das Objekt übergeben wird, sodass seine Elemente mit Sicherheit den richtigen Typ haben.
Dabbler
80
public static <E> E[] appendToArray(E[] array, E item) { ...

Beachten Sie die <E>.

Statische generische Methoden benötigen eine eigene generische Deklaration ( public static <E>), die von der generischen Deklaration ( public class ArrayUtils<E>) der Klasse getrennt ist .

Wenn sich der Compiler über eine Typmehrdeutigkeit beim Aufrufen einer statischen generischen Methode beschwert (in Ihrem Fall ebenfalls nicht wahrscheinlich, aber im Allgemeinen nur für den Fall), können Sie eine statische generische Methode mit einem bestimmten Typ ( _class_.<_generictypeparams_>_methodname_) explizit aufrufen :

String[] newStrings = ArrayUtils.<String>appendToArray(strings, "another string");

Dies würde nur passieren, wenn der Compiler den generischen Typ nicht bestimmen kann, da z. B. der generische Typ nicht mit den Methodenargumenten zusammenhängt.

Bert F.
quelle
10

Sie müssen den Typparameter auf die Methodenebene verschieben, um anzuzeigen, dass Sie eine generische Methode anstelle einer generischen Klasse haben:

public class ArrayUtils {
    public static <T> E[] appendToArray(E[] array, E item) {
        E[] result = (E[])new Object[array.length+1];
        result[array.length] = item;
        return result;
    }
}
axtavt
quelle
1
Dies funktioniert nicht, da Sie den generischen Typ E nicht definiert haben. In diesem Fall muss der generische Typ <E> in der Klassendefinition vorhanden sein.
George Xavier
0

Ich werde es auf einfache Weise erklären.

Auf Klassenebene definierte Generika sind vollständig von den auf (statischer) Methodenebene definierten Generika getrennt.

class Greet<T> {

    public static <T> void sayHello(T obj) {
        System.out.println("Hello " + obj);
    }
}

Wenn Sie den obigen Code irgendwo sehen, beachten Sie bitte, dass das auf Klassenebene definierte T nichts mit dem in der statischen Methode definierten T zu tun hat. Der folgende Code ist ebenfalls vollständig gültig und entspricht dem obigen Code.

class Greet<T> {

    public static <E> void sayHello(E obj) {
        System.out.println("Hello " + obj);
    }
}

Warum muss die statische Methode ihre eigenen Generika haben, die von denen der Klasse getrennt sind?

Dies liegt daran, dass die statische Methode aufgerufen werden kann, ohne die Klasse zu instanziieren. Wenn die Klasse noch nicht instanziiert ist, wissen wir noch nicht, was T ist. Dies ist der Grund, warum die statischen Methoden ihre eigenen Generika haben müssen.

Wenn Sie also die statische Methode aufrufen,

Greet.sayHello("Bob");
Greet.sayHello(123);

JVM interpretiert es wie folgt.

Greet.<String>sayHello("Bob");
Greet.<Integer>sayHello(123);

Beide geben die gleichen Ausgänge.

Hello Bob
Hello 123
Vishnu Vivek
quelle