Почему в этом общем методе есть дополнительный <E>?

Я изучил java generics некоторое время назад, но теперь я изучаю коллекции и нашел код, который я не понимаю. Вот код:

static <E> List<E> nCopies(int n, E value)

Это из класса java.util.Collections.

Мой вопрос в том, почему есть:

<E> List<E>

и не только

List<E>

Очевидно, что я что-то упускаю, может ли кто-нибудь прояснить это для меня?

Ответ 1

В <E> List<E> первый <E> означает, что E является параметром типа. Если бы вы не указали его, тогда Java подумает, что E в E value относится к фактическому классу с именем E и попросит вас его импортировать. См. общие методы.

Ответ 2

Вы используете <E> для обозначения метода, который вы определяете.

Наиболее распространенным примером дженериков является типизированный класс:

public class SomeClass<E> {
    ...
}

Затем, когда вы создаете новый объект этого класса, вы определяете тип непосредственно следующим образом:

new SomeClass<String>();

Таким образом, любой метод в этом классе, который ссылается, будет рассматриваться как String для этого экземпляра.

Теперь рассмотрим статический метод (который не привязан к какому-либо конкретному экземпляру класса), чтобы типизировать этот метод, который вы используете для типизации другого типа, применимого к методам, например:

static <E> List<E> nCopies(int n, E value)

Вы используете <E> перед типом возврата, чтобы сказать, что "этот конкретный метод рассмотрит некоторые E, когда он выполнит". Что будет <E> будет определено при вызове метода:

nCopies(3, "a");

В этом примере <E> будет String, поэтому возвращаемый тип будет List<String>.

Наконец, вы можете даже смешать их оба:

public class SomeClass<E> {
    public <F> void doSomething(E e, F f){
        ...
    }
}

В этом случае, если у вас есть экземпляр SomeClass, E в методе doSomething всегда будет String (для этого экземпляра), но F может быть любым, что вы хотите.

Ответ 3

<E> требуется сообщить компилятору, что вы намерены использовать E как параметр типа, так же, как вы делаете, когда вы делаете общий класс (например, public interface List<E>).

Поскольку в интерфейсе или именах классов нет правила (только условные обозначения), а не правило (только условные обозначения), которые вводят имена параметров, должны быть одним символом, компилятор не знал бы, что вы намереваетесь параметр типа, а не конкретное имя класса.

Изменить

Многие говорили, что это напрямую связано со статическими методами. Это неправда. У вас также может быть метод экземпляра, который является общим для собственных параметров типа (хотя обычно параметры типа будут связаны с параметрами типа класса).

Вот пример того, где вы могли бы это сделать:

public class MyList<E> {

    public <N super E> MyList<N> createCopy() {
        //...
    }
}

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

MyList<Integer> integers = createList(1, 2, 5);
MyList<Number> numbers = integers.createCopy();

Ответ 4

List<E> - это возвращаемый тип для метода, тогда как <E> - это тип, передаваемый в (это определяется компилятором из того, что передается как E value).

static <E> List<E> someMethod(E myObject)
{
    E objectOfMyType = myObject;
    List<E> myList = new ArrayList<E>();
    ...
    return myList; 
}

Это будет вызываться как:

MyObject o = new MyObject();
List<MyObject> myList = SomeClass.someMethod(o);

ИМХО, синтаксис для методов является довольно глупым, но там у вас это есть. Увлекательный учебник Oracle находится здесь: http://download.oracle.com/javase/tutorial/extra/generics/methods.html