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

2022-08-31 19:38:47

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

函数 1:

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

}

函数 2:

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

}

答案 1

第一个签名说: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>)

  • 最后,通配符可以有下限,因此使用列表,您可以使该方法正常工作,而不会为您提供任何有用的东西。当然,这就引出了下一个问题:为什么泛型没有下限?addget

有关更深入的答案,请参阅:何时使用泛型方法以及何时使用通配符?http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203


答案 2

泛型使集合类型更加安全。

List<E>:此处的 E 是 Type 参数,可用于确定列表的内容类型,但存在检查期间内容的方法。Noruntime

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 中的引用。


推荐