在 Scala 中所有值都有一種對應的類型 單例類型 形式: ,返回類型 / 場景1:鏈式API調用時的類型指定 由於 Scala 會將 推斷為當前類(即 ),因此無法完成鏈式調用 場景2:方法中使用 實例作為參數 Note:不可定義為 ~~def c1(obj: Foo)~~,因為 Foo 為單例 ...
在 Scala 中所有值都有一種對應的類型
單例類型
- 形式:
value.type
,返回類型value
/null
場景1:鏈式API調用時的類型指定
class Super { def m1(t: Int) = {println(t); this} def m2(t: Int) = {println(t); this} } // 正常列印 new Super().m1(1).m2(2) class Child extends Super { def c1(t: Int) = {println(t); this} } // 異常 value c1 is not a member of Super new Child().m1(1).c1(2)
由於 Scala 會將
this
推斷為當前類(即Super
),因此無法完成鏈式調用class Super { // 指定返回類型為調用方的 this def m1(t: Int): this.type = {println(t); this} def m2(t: Int): this.type = {println(t); this} } class Child extends Super { def c1(t: Int) = {println(t); this} } // 成功列印 new Child().m1(1).c1(2)
場景2:方法中使用
object
實例作為參數object Foo class Child extends Super { def c1(obj: Foo.type) = { if (obj == Foo) println("foo") this } }
Note:不可定義為
def c1(obj: Foo),因為 Foo 為單例對象,而不是類型
類型投影
- 形式:
Outer#Inner
場景:內部類使用時避免類型約束
class Outer { private val inners = ArrayBuffer[Inner]() class Inner (val arg1: Int) { val l = ArrayBuffer[Inner]() } def add(a: Int) = { val t = new Inner(a) inners += t t } } val a = new Outer val b = new Outer val a1 = a.add(1) val b1 = b.add(1) a1.l += b1 // error: type mismatch;
只需要在定義內部類時指定類型投影即可解決
// 表示適用於任何 Outer 類的 Inner 類 val l = ArrayBuffer[Outer#Inner]()
如果將上述例子改用
List
來實現,並不會報錯,計算結果也會自動進行類型投射
路徑
- 路徑中除最後一部分外,都必須是穩定狀態的,如包名、
object
、val
、this/super/super[S]...
不能包含
var
類型var t = new Outer() //...其他操作 val i = new t.Inner // 由於 t 可能會變更,編譯器無法確定其含義
a.b.c.T 內部被翻譯成類型投射 a.b.c.type#T
類型別名
- 形式:
type SomeAliasName
- 必須定義在
class
或object
內部 好處: 在引用類型時可以更加簡潔
class Book { import scala.collection.mutable._ // 為該類型取一個別名 type Index = HashMap[String, Int] // 使用時不在需要重覆的定義複雜的數據類型 val map: Index = new Index() } new Book().map // scala.collection.mutable.HashMap[String,Int]
結構類型
為抽象方法、欄位、類型的定義某種規範
def appendLines(target: { def append(str: String): Any }, lines: Iterable[String]) { for (l <- lines) { // 此次 Scala 使用反射調用該方法 target.append(l); target.append("\n") } }
該方法第一個參數
target
即結構類型,表示使用任何包含該append
方法的實例作為參數傳入。由於反射的代價較大,不到萬不得已不建議使用,如,有通用行為(
append
),卻無法共用trait
組合類型 / 交集類型
- 形式:
T1 with T2 with T3 ...
當需要提供多個特質時使用,即用於約束類型
val image = new ArrayBuffer[java.awt.Shape with java.io.Serializable] val rect = new Rectangle(5, 10, 20, 30) image += rect // 正確,Rectangle 可序列化 image += new Area(rect) // 錯誤 Area 不可序列化
組合類型中也可使用結構類型
Shape with Serializable { def contains(p: Point): Boolean }
中綴類型
- 其實只是一種語法寫法,如
String Map Int
可代替Map[String, Int]
可參考數學運算中的表達方式
type x[A, B] = (String, Int) // 即可使用 String x Int 來表示 (String, Int)
一般中綴類型操作符都是左關聯的,除了前面提到的
:
操作符,這個是右關聯的,比如List
的操作
中綴類型名稱可以是任意操作符,除了
*
,避免與類型定義衝突