Ответ 1
В <E> List<E>
первый <E>
означает, что E
является параметром типа. Если бы вы не указали его, тогда Java подумает, что E
в E value
относится к фактическому классу с именем E
и попросит вас его импортировать. См. общие методы.
Я изучил java generics некоторое время назад, но теперь я изучаю коллекции и нашел код, который я не понимаю. Вот код:
static <E> List<E> nCopies(int n, E value)
Это из класса java.util.Collections
.
Мой вопрос в том, почему есть:
<E> List<E>
и не только
List<E>
Очевидно, что я что-то упускаю, может ли кто-нибудь прояснить это для меня?
В <E> List<E>
первый <E>
означает, что E
является параметром типа. Если бы вы не указали его, тогда Java подумает, что E
в E value
относится к фактическому классу с именем E
и попросит вас его импортировать. См. общие методы.
Вы используете <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 может быть любым, что вы хотите.
<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();
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