看板 PLT 關於我們 聯絡資訊
※ 引述《macbuntu (邀怪)》之銘言: : interface A<T> { : public T get(); : public void set(T t); : } : : static void func(A<? extends Number> a) { : Number n = a.get(); // OK : a.set(n); // compile time error : a.set(123); // compile time error : a.set(new Object()); // compile time error : a.set(null); // only this is OK : } : : 上面那個 func() 裡面, 如果沒有前三個 set() 呼叫, 是可以 compile 沒問題的. : 但是 T 用在 parameter type 的時候根本不該允許 <? extends Number> : 這種 covariant binding, 也就是說, Java compiler 允許 A 處在一個不正確 : 的狀態, 讓 A.set() 變成完全無用, 只能放 null 進去. 只允許 s.set(null) : 從語意上就變得說不通了, 原先的 A<T> 可以 a.set(a.get()) 為什麼 : 進到 func() 裡後不能 a.set(a.get())? 是因為 A 裡的 T 只該允許 invariant. 我現在還是不能理解,為何你會把上述的例子當作是語法上的瑕疵。 我舉幾個例子: static void func(A<? extends Number> a) { Number n = a.get(); a.set(n); // 應該要 OK 比較直覺 } 如果你把文脈考慮進來,會覺得會有編譯錯誤似乎不太對,但是如果你以編譯器的 觀點,或是 runtime bytecode 層面去看: static void func(A<? extends Number> a) { // 姑且不論值從哪裡來,VM 在 runtime 只知道 n 的 value 是 Number ref Number n = ...; // n could hold Integer, Double...etc ref value a.set(n); // 應該要 OK 嗎? } 如果你還是覺得變數的 n 的確切型別編譯器可以從 context deduce 出來,暫時把 例子想成: static void func(A<? extends Number> a, Number n) { a.set(n); } 當你看到 client code: func(new A<Float>()); // func(new A<Double>(), new Integer(87)); 編譯器不放行是對的。 ※ 引述《godfat (godfat 真常)》之銘言: : 我覺得到了這邊的話,可能就要回頭來看 variance annotation 原本 : 所表達出的語意,也就是 A[+T] 時表示如果 U < T, 則 A[U] < A[T] : 所以你能在 scala 裡做這件事,卻不能在 java 裡做: : : val int_list: List[Integer] = List(1,2,3) : val any_list: List[Any] = int_list : : 雖然 java array 是可以的,因此 java array 本身似乎帶有 +T 的意義? : 也就是說並不只是在 argument 上可以有 variance, value/variable 也是可以的。 formal parameter 本質上就是 local variable,所以在 Java 中也是可以這樣做: java.util.List<Integer> int_list = new java.util.ArrayList<Integer>(); java.util.List<? extends Object> any_list = int_list; 這時候可不能說:可是在 Java case 裡,any_list "卻" 沒有辦法放進任何東西。 因為 scala case 中,any_list 一樣不能放進任何東西,你說『scala 中的 List 本來就是設計成 immutable』,我會說『就因為 scala List 設計成 immutable 所以你才能有一個 List[+T],讓你在 scala 中能以比較短的寫法 List[Any] 作跟 Java List<? extends Object>(寫法比較長) 一樣多的事』。你能夠有 covariant subtyping 的 List[+T],是可遇不可求的。 那反過來,如果假設 scala List 當初也是設計成 mutable 容器(也就是 List[T]), 那麼我可以在 Java 裡這樣子使用 local variable: java.util.List<Integer> int_list = new java.util.ArrayList<Integer>(); java.util.List<? extends Object> any_list = int_list; System.out.println(any_list.get(0)); 請問,在 scala 中該怎麼作? ======================================================================== 一來一往到這一篇,我是覺得有點亂了,或許每個人想要交流的看法並沒有真的 有交流到。 Java Generics 最後採用的語法的確是不容易掌握(也就是不直覺),我認為會造成 這樣子的原因在於,當初 Java team 進行引入 generics 的一個主要原則: 令 JVM (spec) 必要的修改最小,不是令它的語法既簡單又美 以最後實現在 Java 1.5 中的 generics 來說,JVM 規格配合 generics 而作的 修改,真的是蠻小的。主要是增加 type parameter, type bounds 這些資訊 進 class/method bytecode,讓 compiler 可以讀取到,JVM 的 instruction 沒有 任何一個有修改。 最後,我認為每一個 Java programmer,沒有看過這兩個 paper(除非完全不用到 Java Generics)的人,都應該花時間看看: Adding Wildcards to the Java Programming Language http://www.jot.fm/issues/issue_2004_12/article5.pdf On Access Restriction with Java Wildcards http://www.jot.fm/issues/issue_2005_12/article6.pdf -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 218.173.129.21