作者jtorngl (DDFL)
看板java
标题[问题] 请问泛型的问题
时间Sat Aug 29 12:31:55 2020
请问 Collection 不支援 covariance (还是该说泛型不支援 covariance)
所以 summary 只能传入 List<Number> 的物件
而不能传入 List<Integer>、List<Double>, ...
public double summary(List<Number> nums) {
double total = 0;
for (Number num : nums) {
total += num.doubleValue();
}
return total;
}
为了让这段程式能达到类似 covariance 机制
所以会使用 wildcard
public double wildcardSummary(List<? extends Number> nums) {
double total = 0;
for (Number num : nums) {
total += num.doubleValue();
}
return total;
}
不过泛型使用 extends 一样可以做到不是吗
private <E extends Number> double genericsSummary(List<E> nums) {
double total = 0;
for (Number num : nums) {
total += num.doubleValue();
}
return total;
}
目前能想到的只有,wildcard 有 super 来达到 contravariance
但是泛型只支援 extends 而没有支援 super
那如果没有要 contravariance 的效果
有什麽情况是 wildcard 才能做到,而泛型还是不能编译的?
目前看一些文章,在方法的参数,要限制参数型态边界时
几乎都是使用 List<? extends Number>
好像比较少看到 <E extends Number> ... List<E>
看了一下 JDK 的 List interface
boolean addAll(Collection<? extends E> c);
是因为限制的参数型态都是动态,才只能用 wildcard 吗?
补充一下,问这个问题主要是,目前开始接触 sonarcube 这类工具
对於程式会有很多规范,虽然目前还没去看是否有泛型语法的 issue
只是想先问一下,实际开发时,是否在需要限制参数边界时
一律使用 wildcard,而不要用泛型宣告
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 114.43.70.113 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/java/M.1598675517.A.17E.html
※ 编辑: jtorngl (114.43.70.113 台湾), 08/29/2020 12:36:01
1F:→ ssccg: 在你举的method参数,只用在一个地方的情况下,两个一样 08/29 13:14
3F:→ ssccg: methods.html 官方文件是建议在这个情况下用wildcard 08/29 13:16
4F:→ ssccg: wildcard才能做到的如你说的就是下限(super),另外就是不需 08/29 13:19
自己在写时都没有限定参数边界过
一直都是用 public <E> void process(List<E> datas)
最近看了某些 API 的 source,突然才想到一直没搞懂泛性这块
查了一些资料,看到 covariant、contravariant、invariant
现在反而更混乱,有一种若要直接下结论的话
反正就是要限定参数边界时,不管上限或下限,用 wildcard 就对了
5F:→ ssccg: 要type parameter可以直接用在field、local variable 08/29 13:19
6F:→ ssccg: 而generic的type parameter可以用在多个参数,可有多个上限 08/29 13:30
请问不需要 type parameter 可以直接用在 field 是什麽意思
泛型可以用在 field,但这句话不是指 wildcard 可以用在 field 吧?
感谢说明
7F:→ ssccg: wildcard可以用在field没错啊,上面的网页最下面也有范例 08/30 02:00
8F:→ ssccg: generic要用在field,必须要class有type parameter 08/30 02:03
9F:→ ssccg: 而wildcard可直接用像 class A {List<? extends B> list;} 08/30 02:05
10F:→ ssccg: 如果不是真的需要generic class,这时wildcard就比较适合 08/30 02:06
11F:→ ssccg: 而method部分也不是wildcard就对了,上面也说了限制有多个 08/30 02:09
12F:→ ssccg: 或多个参数时就只能用generic 08/30 02:12
13F:→ ssccg: 其实两个语意就是有差,wildcard是未知,顶多加上一些限制 08/30 02:14
14F:→ ssccg: generic是写其他程式用到再决定,但用到後就是确定的 08/30 02:18
15F:→ ssccg: 更正一下最後两个generic → type parameter 08/30 02:55
感谢 ssccg 大的说明
如果先不论语法,编译检查等
单纯只论为什麽要用 wildcard
以JDK 的 List 介面来说
public interface List<E> extends Collection<E>
有一个方法是 boolean containsAll(Collection<?> c);
如果是这样定义的
boolean containsAll(Collection<E> c);
那当我们建立 List<String> strs = Arrays.asList("1", "2");
那这个 strs 的 containsAll 方法,只能用来比较 String 集合了
反之因为使用 ? 定义
boolean containsAll(Collection<?> c); 这样定义
所以 strs 还是可以用来比对各种型态的集合
我想一开始会不懂 <T> 和 ? 的差别
应该是在 "定义" 和 "使用" 没搞清楚差别
1. ? 必须用在已经使用在 <T> 定义好的类别之,只是它可是任意型态
2. 在 generics class,<T> 和 ? 的使用就会有如上面 containsAll 的差别
但是在泛型方法中,应该就没有泛型类别上的限制
public class XyzUtil {
public static <T> void process(T t) {
System.out.println(t);
}
}
在使用 process(T t) 时,每次呼叫方法时,才决定参数的型态
所以这时候使用 <T> 和 ? 的行为就是一样的
也就是此时的 T 也相当於任意型态,不知这样理解对吗
因为一直以来我都只有使用 generics method
所以觉得 <T> 和 ? 不是一样的东西吗
除了在限制参数边界时,? 可以使用 super
只是我会用 <T extends Number> 之类别,却还没用过 super
所以也就一直没研究 <T> 和 ? 的差别
※ 编辑: jtorngl (114.43.70.113 台湾), 08/30/2020 16:48:07
16F:→ tw11509: Producer Extends, Consumer Super 关键字,可以参考一 08/31 11:54
17F:→ tw11509: 下 08/31 11:54
18F:→ tw11509: 对我来说,通常不用去管泛型类别时,我会选择用wildcard 08/31 12:48
19F:→ tw11509: ,例如:Collections#swap,交换List中的元素根本不需要 08/31 12:48
20F:→ tw11509: 知道里面装了什麽类别的元素 08/31 12:48
感谢说明,也有在看 <? exends T> 和 <? super T>
原本只是以为单纯的设定型别的上下界
但是还有 set 和 get 使用上的限制,而且有点不好懂
我才知道原来自己对泛型的理解还很浅
像是
Number[] nums = new Integer[3]; // ok
List<Number> nums = new ArrayList<Integer>(); // compile error
语法的限制,或是编译器的限制等等的细节
都没去理解,还有得学
※ 编辑: jtorngl (111.249.70.26 台湾), 08/31/2020 13:08:52