利用有限制性通配符来提高API的灵活性——《effective Java》第三版

本文介绍了Java中泛型通配符PECS(Producer Extends, Consumer Super)原则,用于优化生产者和消费者类型的参数。通过PECS,可以确保方法接受正确的输入并拒绝不匹配的类型。例如,`pushAll`方法的参数应为`Iterable<? extends E>`,而`popAll`的参数应为`Collection<? super E>`。应用这一原则可以提高代码的灵活性和类型安全性,避免编译错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型。如果某个输入参数既是生产者,又是消费者,那么通配符类型对你就没有什么好处了:因为你需要的是严格的类型匹配,这是不用任何通配符而得到的。下面的助记符便于让你记住要使用哪种通配符类型:PECS 表示 producer-extends,consumer-super。

       换句话说,如果参数化类型表示一个生产者 T,就使用<?extends T>;如果它表示一个消费者 T,就使用<?super T>。在我们的 Stack 示例中,pushAll的 src 参数产生日实例供 Stack 使用,因此 src 相应的类型为Iterable<? extends E>;popAl1

的 dst 参数通过 Stack 消费日实例,因此 dst 相应的类型为Collection<?super E>。PECS 这个助记符突出了使用通配符类型的基本原则。Naftalin 和 Wadler 称之为 Get and Put Principle [Naftalin07, 2.4]。

        记住这个助记符,下面我们来看一些之前的条目中提到过的方法声明。第 28 条中的reduce 方法就有这条声明:

public Chooser(Collection<T> choices)

         这个构造器只用 choices 集合来生成类型 T 的值(并把它们保存起来供后续使用),因此它的声明应该使用一个 extends T 的通配符类型。得到的构造器声明如下:

// Wildcard type for parameter that serves as an T producer

public Chooser(Collection<? extends T> choices)

        这一变化实际上有什么区别吗?事实上,的确有区别。假设你有一个List<Integer>,

想通过Function<Number>把它简化。它不能通过初始声明进行编译,但是一旦添加了有限制的通配符类型,就可以进行编译了。现在让我们看看第 30 条中的 union 方法。声明如下:

public static <E> Set<E> union(Set<E> s1, Set<E> s2)

s1 和 s2 这两个参数都是生产者 E,因此根据 PECS 助记符,这个声明应该是:

public static <E> Set<E> union(Set<? extends E> s1,Set<? extends E> s2)

       注意返回类型仍然是 Set<E>。不要用通配符类型作为返回类型。除了为用户提供额外的灵活性之外,它还会强制用户在客户端代码中使用通配符类型。修改了声明之后,这段代码就能正确编译了:

Set<Integer>    integers = Set.of(1, 3, 5);

Set<Double>    doubies = Set.of(2.0, 4.0, 6.0);

Set<Number>    numbers = union(integers, doubles);

       如果使用得当,通配符类型对于类的用户来说几乎是无形的。它们使方法能够接受它们应该接受的参数,并拒绝那些应该拒绝的参数。如果类的用户必须考虑通配符类型,类的API 或许就会出错。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值