泛型类型和通配符类型之间的区别

时间:2012-06-08 04:38:50

标签: java generics wildcard

我是Generic的新手,我的问题是:两个函数之间有什么区别:

功能1:

public static <E> void funct1  (List<E> list1) {

}

功能2:

public static void funct2(List<?> list) {

}

感谢。

7 个答案:

答案 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;通过与逻辑量化的比较,即通用量化和存在量化。

  • 对应于“forall E,...”
  • 对应于“存在某种东西(表示为......)......”

因此,以下通用方法声明意味着,对于所有类类型 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是包含方法的类的名称。)