【发布时间】:2011-12-28 07:44:12
【问题描述】:
前段时间学过java泛型,现在在学集合,发现了一些看不懂的代码。代码如下:
static <E> List<E> nCopies(int n, E value)
来自java.util.Collections类。
我的问题是为什么会有:
<E> List<E>
不仅如此
List<E>
显然我遗漏了一些东西,有人可以为我澄清一下吗?
【问题讨论】:
前段时间学过java泛型,现在在学集合,发现了一些看不懂的代码。代码如下:
static <E> List<E> nCopies(int n, E value)
来自java.util.Collections类。
我的问题是为什么会有:
<E> List<E>
不仅如此
List<E>
显然我遗漏了一些东西,有人可以为我澄清一下吗?
【问题讨论】:
List<E> 是方法的返回类型,而<E> 是传入的类型(这是编译器根据E value 传递的内容推断出来的)。 p>
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
【讨论】:
String myStringArg。例如,如果您有两个类型参数,它可能看起来像 <E, TArg2> 而不仅仅是 <E>。
在<E> List<E> 中,第一个<E> 表示E 是一个类型参数。如果您没有指定它,那么 Java 会认为 E value 中的 E 引用了一个名为 E 的实际类,并要求您导入它。见generic methods。
【讨论】:
static List<E> nCopies(int n, <E> value)。像我上面写的那样,Java 编程语言的设计者这样做有什么问题吗?对我来说,表示泛型变量的方式更清晰。
<E extends F> 的参数,它会重复;最好把它放在前面,然后在任何地方使用<E>。
<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();
【讨论】:
您使用<E> 来代表您定义的方法。
泛型最常见的例子是有一个这样的典型类:
public class SomeClass<E> {
...
}
然后,当您创建该类的新对象时,您可以像这样直接定义类型:
new SomeClass<String>();
这样,该类中引用<E> 的任何方法都会将<E> 视为一个字符串。
现在考虑一个静态方法(它不绑定到类的任何特定实例),为了典型化该方法,您使用另一种适用于方法的典型化,如下所示:
static <E> List<E> nCopies(int n, E value)
您在返回类型之前使用<E> 表示“此特定方法在执行时将考虑一些 E”。 <E> 将在你调用该方法时决定:
nCopies(3, "a");
在此示例中,<E> 将是一个字符串,因此返回类型将是一个List<String>。
最后,您甚至可以将它们混合使用:
public class SomeClass<E> {
public <F> void doSomething(E e, F f) {
...
}
}
在这种情况下,如果您有 SomeClass 的实例,则 doSomething 方法中的 E 将始终为 String(对于该实例),但 F 可以是您想要的任何值。
【讨论】:
简单来说:表示E 不是一个类。如果E 是一个类(或接口),List<E> 将是一个有效的返回类型——尽管不推荐,一个类可以用单个字母命名(因为类型变量也可以用更多字母命名,即使使用现有的类名,混淆任何人:static <Integer> List<Integer> method() {...})。
【讨论】: