Как сделать статический метод Java Generic?

173

Ниже приведен фрагмент о том, как сделать универсальный класс Java для добавления одного элемента в массив. Как я могу сделать appendToArray статическим методом. Добавление статического в сигнатуру метода приводит к ошибкам компиляции.

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;
        }
}
Крис Джонсон
источник
Какие ошибки компиляции вы получаете? Кроме того, почему бы просто не использовать один из стандартных контейнеров библиотеки?
Карл Кнехтель
1
Ошибка компиляции: я на самом деле неправильно добавлял статический модификатор. Использование коллекций: Да, использование коллекции было бы идеальным, но вопрос не о коллекциях против массивов, мой вариант использования требует массива.
Крис Джонсон
Обратите внимание, что вам нужно будет использовать (EVIL) отражение, чтобы прекратить выдачу клиентским кодом исключения в некоторых, но не во всех случаях (приятно). Лучше избегать ссылочных массивов.
Том Хотин - tackline

Ответы:

283

единственное, что вы можете сделать, это изменить свою подпись на

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

Важные детали:

Общие выражения, предшествующие возвращаемому значению, всегда вводят (объявляют) новую переменную универсального типа.

Кроме того, переменные типа между типами ( ArrayUtils) и статическими методами ( appendToArray) никогда не мешают друг другу.

Итак, что же это значит: В моем ответе <E>скрыть бы Eот , ArrayUtils<E>если метод не будет static. И не <E>имеет ничего общего с Eот ArrayUtils<E>.

Чтобы лучше отразить этот факт, более правильным ответом будет:

public static <I> I[] appendToArray(I[] array, I item)
scheffield
источник
30
Также имейте в виду, что нет абсолютно никакой связи между переменной типа уровня класса Eи статической переменной типа метода E. Я считаю, что гораздо лучше использовать другое имя переменной при объявлении обобщенных методов, статических или иных, внутри обобщенных классов.
Судья Ментал
но в этом случае я могу передать один объект разных типов в параметры. Как я могу передать массив Integer [] в качестве первого параметра и элемента Double.
Розовая пантера
pinkpanther: True, но это не приносит никакого вреда, потому что статический метод всегда работает только с объектом массива, который передается ему через параметр, поэтому его элементы обязательно имеют правильный тип.
Дабблер
80
public static <E> E[] appendToArray(E[] array, E item) { ...

Обратите внимание <E>.

Статические обобщенные методы нуждаются в собственном обобщенном объявлении ( public static <E>), отдельном от обобщенного объявления класса ( public class ArrayUtils<E>).

Если компилятор жалуется на неоднозначность типа при вызове статического универсального метода (опять-таки маловероятно в вашем случае, но, вообще говоря, на всякий случай), вот как явно вызвать статический универсальный метод с использованием определенного типа ( _class_.<_generictypeparams_>_methodname_):

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

Это может произойти, только если компилятор не может определить универсальный тип, потому что, например, универсальный тип не связан с аргументами метода.

Берт F
источник
10

Вам нужно переместить параметр типа на уровень метода, чтобы указать, что у вас есть универсальный метод, а не универсальный класс:

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
источник
1
Это не будет работать, потому что вы не определили универсальный тип E. В этом случае вам все еще нужно иметь универсальный тип <E> в определении класса.
Джордж Ксавье
0

Я объясню это простым способом.

Обобщения, определенные на уровне класса, полностью отделены от обобщения, определенного на (статическом) уровне метода.

class Greet<T> {

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

Когда вы видите приведенный выше код где-либо, обратите внимание, что T, определенный на уровне класса, не имеет ничего общего с T, определенным в статическом методе. Следующий код также полностью действителен и эквивалентен приведенному выше коду.

class Greet<T> {

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

Почему статический метод должен иметь свои собственные обобщения, отличные от обобщенных классов?

Это потому, что статический метод может быть вызван даже без создания экземпляра класса. Поэтому, если класс еще не создан, мы еще не знаем, что такое T. Именно поэтому статические методы должны иметь свои собственные обобщения.

Итак, всякий раз, когда вы вызываете статический метод,

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

JVM интерпретирует это следующим образом.

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

Оба дают одинаковые выводы.

Hello Bob
Hello 123
Вишну Вивек
источник