我是Generic的新手,我的问题是:两个函数之间有什么区别:
功能1:
public static <E> void funct1 (List<E> list1) {
}
功能2:
public static void funct2(List<?> list) {
}
感谢。
答案 0 :(得分:34)
第一个签名是: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
答案 1 :(得分:7)
泛型使集合更安全。
List<E>
:这里的E是类型参数,可用于确定列表的内容类型,但有No
方式检查{{1}期间的内容是什么}。
runtime
Generics are checked only during compilation time.
:这是专门构建到java中的,用于处理Type Parameter的问题。 <? extends String>
表示此列表可以
"? extends String"
例如:
动物类 狗类延伸动物 Tiger类扩展了Animal
所以使用objects which IS-A String.
将"public void go(ArrayList<Animal> a)"
狗或虎作为其内容,但动物。
NOT accept
是制作"public void go(ArrayList<? extends Animal> a)"
检查Head First Java中的引用。
答案 2 :(得分:1)
作为参数类型的列表表示参数必须是具有任何对象类型的项列表。此外,您可以绑定E
参数以声明对函数体内列表项的引用。
作为参数类型的List具有相同的语义,除了除了使用Object
之外无法声明对列表中项目的引用。其他帖子提供了额外的细微差别。
答案 3 :(得分:1)
第一个是接受必须是E类型项目列表的参数的函数。
未定义第二个示例类型
List<?> list
因此您可以传递任何类型对象的列表。
答案 4 :(得分:1)
我通常会解释&lt; E &gt;之间的区别和&lt; ?&gt;通过与逻辑量化的比较,即通用量化和存在量化。
因此,以下通用方法声明意味着,对于所有类类型 E ,我们定义funct1
public static <E> void funct1 (List<E>; list1) {
}
以下泛型方法声明意味着,对于由&lt; ?&gt;表示的某些现有类,我们定义funct2
。
public static void funct2(List<?> list) {
}
答案 5 :(得分:0)
(自编辑以来)这两个函数签名对外部代码具有相同的效果 - 它们都将List
作为参数。通配符等效于仅使用一次的类型参数。
答案 6 :(得分:0)
除了之前提到的那些差异之外,还有一个额外的区别:您可以显式设置泛型方法调用的类型参数:
List<Apple> apples = ...
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok
// with type parameters, even though the method has none
ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple>
// cannot be converted to List<Banana>
(ClassName
是包含方法的类的名称。)