Jak ustawić statyczną metodę Java Generic?

172

Poniżej znajduje się fragment pokazujący, jak utworzyć klasę ogólną java, aby dołączyć pojedynczy element do tablicy. Jak mogę uczynić appendToArray metodą statyczną. Dodanie wartości statycznej do podpisu metody powoduje błędy kompilacji.

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
źródło
Jakie błędy kompilacji otrzymujesz? Dlaczego po prostu nie użyć jednego ze standardowych kontenerów bibliotecznych?
Karl Knechtel
1
Błąd kompilacji: Właściwie dodałem modyfikator statyczny niepoprawny. Korzystanie z kolekcji: Tak, użycie kolekcji byłoby idealne, ale pytanie nie dotyczy kolekcji i tablic, mój przypadek użycia wymaga tablicy.
Chris Johnson,
Zauważ, że musisz użyć odbicia (EVIL), aby kod klienta nie wyrzucał wyjątku w niektórych, ale nie we wszystkich okolicznościach (ładne). Najlepiej unikać tablic referencyjnych.
Tom Hawtin - tackline

Odpowiedzi:

283

jedyne, co możesz zrobić, to zmienić swój podpis na

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

Ważne szczegóły:

Wyrażenia ogólne poprzedzające wartość zwracaną zawsze wprowadzają (deklarują) nową zmienną typu ogólnego.

Ponadto zmienne typu między typami ( ArrayUtils) i metodami statycznymi ( appendToArray) nigdy nie kolidują ze sobą.

Więc co to oznacza: w mojej odpowiedzi <E>ukryłbym Eprzed, ArrayUtils<E>gdyby metoda nie była static. I <E>nie ma nic wspólnego z Efrom ArrayUtils<E>.

Aby lepiej odzwierciedlić ten fakt, bardziej poprawną odpowiedzią byłoby:

public static <I> I[] appendToArray(I[] array, I item)
scheffield
źródło
30
Należy również pamiętać, że nie ma absolutnie żadnego związku między zmienną typu na poziomie klasy Ea zmienną typu metody statycznej E. Uważam, że znacznie lepszą praktyką jest użycie innej nazwy zmiennej podczas deklarowania metod ogólnych, statycznych lub innych, wewnątrz klas ogólnych.
Sędzia Mental
ale w tym przypadku mogę przekazać do parametrów jeden obiekt różnych typów. Jak mogę przekazać tablicę Integer [] jako pierwszy parametr i element Double.
pinkpanther
pinkpanther: Prawda, ale nie wyrządza żadnej szkody, ponieważ metoda statyczna zawsze działa tylko na obiekcie tablicy, który jest przekazywany do niej przez parametr, więc jej elementy z pewnością mają poprawny typ.
Dabbler
80
public static <E> E[] appendToArray(E[] array, E item) { ...

Zwróć uwagę na <E>.

Statyczne metody generyczne wymagają własnej deklaracji ogólnej ( public static <E>) oddzielnej od deklaracji ogólnej klasy ( public class ArrayUtils<E>).

Jeśli kompilator skarży się na niejednoznaczność typu podczas wywoływania statycznej metody ogólnej (znowu nie jest to prawdopodobne w twoim przypadku, ale ogólnie mówiąc, na wszelki wypadek), oto jak jawnie wywołać statyczną metodę ogólną przy użyciu określonego typu ( _class_.<_generictypeparams_>_methodname_):

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

Dzieje się tak tylko wtedy, gdy kompilator nie może określić typu ogólnego, ponieważ np. Typ ogólny nie jest powiązany z argumentami metody.

Bert F.
źródło
10

Musisz przenieść parametr typu na poziom metody, aby wskazać, że masz metodę ogólną, a nie klasę ogólną:

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
źródło
1
To nie zadziała, ponieważ nie zdefiniowałeś typu ogólnego E. W takim przypadku nadal musisz mieć typ ogólny <E> w definicji klasy.
George Xavier
0

Wyjaśnię to w prosty sposób.

Typy ogólne zdefiniowane na poziomie klasy są całkowicie niezależne od typów ogólnych zdefiniowanych na (statycznym) poziomie metody.

class Greet<T> {

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

Kiedy zobaczysz powyższy kod w dowolnym miejscu, zwróć uwagę, że T zdefiniowane na poziomie klasy nie ma nic wspólnego z T zdefiniowanym w metodzie statycznej. Poniższy kod jest również całkowicie ważny i równoważny z powyższym kodem.

class Greet<T> {

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

Dlaczego metoda statyczna musi mieć własne typy ogólne, oddzielne od metod Class?

Dzieje się tak, ponieważ metodę statyczną można wywołać bez tworzenia wystąpienia klasy. Jeśli więc klasa nie została jeszcze utworzona, nie wiemy jeszcze, co to jest T. To jest powód, dla którego metody statyczne muszą mieć swoje własne typy.

Tak więc za każdym razem, gdy wywołujesz metodę statyczną,

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

JVM interpretuje to następująco.

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

Oba dają te same wyniki.

Hello Bob
Hello 123
Vishnu Vivek
źródło