關於scala的類型推斷前面已經提到過多次。再來看一下下麵這個例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var list2 = new ArrayList[Int] list2 add 1 list2 add 2... ...
關於scala的類型推斷前面已經提到過多次。再來看一下下麵這個例子:
import java.util._ var list1: List[Int] = new ArrayList[Int] var list2 = new ArrayList[Int] list2 add 1 list2 add 2 var total = 0 for (index <- 0 until list2.size()) { total += list2.get(index) } println("Total is " + total)
在這段代碼中新建了兩個ArrayList實例。第一個實例list1的聲明使用了顯式卻冗餘的類型聲明,第二個實例list2的生命則依賴了scala的類型推斷。
再註意下第一行的導入語句,import語句里的下劃線等價於java導入里的“*”。在scala導入語句中使用java.util._則表示導入java.util包下的所有類。如果scala導入語句中的下劃線是跟在一個類名後,則表示導入類中的所有成員——等價於Java中的靜態導入。
看一下程式的執行結果:
一方面scala提供了類型推斷功能讓我們可以簡單地聲明類實例;另一方面,scala在類型轉換方面也顯得非常警覺,嚴禁進行可能引發類型問題的轉換,如下麵的代碼:
/** * Created by robin on 2016/6/21. */ import java.util._ var list1 = new ArrayList[Int] var list2 = new ArrayList list2 = list1 // Compilation Error
在代碼中先是創建了一個ArrayList[Int]的實例list1,而後又創建了一個不帶類型參數的ArrayList實例list2(實際上是創建了一個ArrayList[Nothing]的實例)。最後將list1賦值給list2,因為這一步,程式在編譯時會報錯,看一下:
如果是對應的Java代碼在編譯時不會報錯,但是在執行時會拋出ClassCastException異常(因為Java的泛型實現機制)。
在scala中,Nothing是所有類的子類。父類是不能作為子類的實例的,所以這裡會報錯。
那麼如何創建一個不指定類型的ArrayList實例呢?想想Java。使用Object是一個方向,但是在scala中卻不甚規範。應該是在上一篇文中提到過:在scala中Any類是所有類的基類——如同Object在Java中的角色。
將上面的代碼略作調整:
import java.util._ var list1 = new ArrayList[Int] var list2 = new ArrayList[Any] var ref1 : Int = 1 var ref2 : Any = null ref2 = ref1 //OK list2 = list1 // Compilation Error
在新的代碼里list1是ArrayList[Int]的實例, list2是ArrayList[Any]的實例。此外還創建了兩個新的實例:Int實例ref1、Any實例ref2。將ref1賦值給ref2是沒有錯的,這等價於將Integer的值賦給Object對象。但是將list1賦值list2卻依然會報錯。
學習scala的時候,將ArrayList等集合對象視為容器。Scala不允許將一個持有任意類型實例的容器賦給一個持有Any實例的容器。
這一節有如下三點比較重要:
- 在使用scala時,只要是有意義的地方,都可以依賴類型推演。
- Scala認為無參數化類型的容器是Nothing的容器,並且限制類型間的賦值。
- 預設情況下,Scala不允許將一個持有任意類型實例的容器賦給一個持有Any實例的容器。
這幾點結合起來,可以增強編譯時的類型安全。
#############