我是Generic的新手,我的问题是:两个功能之间有什么区别:
功能1:
public static <E> void funct1 (List<E> list1) {
}
功能2:
public static void funct2(List<?> list) {
}
第一个签名说:list1是一个ES列表。
第二个签名说:list是某种类型的实例的列表,但我们不知道类型。
当我们尝试更改方法时,区别变得明显,因此它需要第二个参数,应将其添加到方法内部的列表中:
import java.util.List;
public class Experiment {
public static <E> void funct1(final List<E> list1, final E something) {
list1.add(something);
}
public static void funct2(final List<?> list, final Object something) {
list.add(something); // does not compile
}
}
第一个效果很好。而且您不能将第二个参数更改为可以实际编译的任何内容。
实际上我只是发现了一个更好的区别说明:
public class Experiment {
public static <E> void funct1(final List<E> list) {
list.add(list.get(0));
}
public static void funct2(final List<?> list) {
list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!!
}
}
[一个可能为什么我们只需要限制我们可以使用<?>
的原因(就像@Babu_Reddy_H在注释中所做的那样)。我看到了通配符版本的以下好处:
调用者必须对他传入的对象了解得更少。例如,如果我有一个列表列表:Map<String, List<?>>
,我可以将其值传递给您的函数,而无需指定列表元素的类型。所以
如果我分发像这样参数化的对象,我会积极限制人们对这些对象的了解以及它们可以做什么(只要它们远离不安全的铸造)。
当我将它们组合在一起时,这两个是有意义的:List<? extends T>
。例如,考虑一种方法List<T> merge(List<? extends T>, List<? extends T>)
,该方法将两个输入列表合并为一个新的结果列表。当然可以引入另外两个类型参数,但是为什么要这么做呢?这将超过指定的东西。
add
方法起作用,而get
则没有任何用处。当然,这触发了下一个问题:为什么泛型没有下界?有关更深入的答案,请参见:When to use generic methods and when to use wild-card?和http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203
泛型使集合的类型更加安全。
List<E>
:E,这里是类型参数,可以用来确定列表的内容类型,但是有No
种方法可以检查runtime
期间的内容。
Generics are checked only during compilation time.
<? extends String>
:这是Java专门构建的,用于处理类型参数的问题。 "? extends String"
表示此列表可以具有
objects which IS-A String.
例如:
动物类狗类延伸动物老虎类延伸动物
因此,使用"public void go(ArrayList<Animal> a)"
将NOT accept
狗或老虎作为其内容,但将其作为动物。
"public void go(ArrayList<? extends Animal> a)"
是制作ArrayList take in Dog and Tiger type.
所需的内容>
在Head First Java中检查引用。
第一个是接受参数的函数,该参数必须是E类型的项目的列表。
作为参数类型的列表表示,该参数必须是具有任何对象类型的项目的列表。此外,您可以绑定E
参数来声明对函数体内列表项的引用。
我通常通过与逻辑量化(即通用量化和存在量化)进行比较来解释<< [E] >>和<< [?] >>之间的区别。
除了前面提到的那些差异之外,还存在其他差异:您可以为泛型方法的调用显式设置类型参数:
ClassName
是包含方法的类的名称。)