作者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/m.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