新特性的起源
要說Type Specialization的起源,還是和泛型的類型擦除和自動裝箱拆箱有關。我們知道,泛型類型擦除後,一般在內部會用它的上界類型來替代,通常是Object。然而,在Java中,基本類型與對象類型是不能相互引用的,這也是爲什麼泛型中不能使用基本類型的原因。所以,如果我們想在泛型中指定基本類型,就要用其對應的對象類型來替代,比如int類型,我們就需要用Integer來替代。這樣就帶來了更大的問題,看看下面的Java代碼:ArrayList<Integer> list = new ArrayList<Integer>(); list.add(1); |
Type Specialization就是爲了解決這個問題而產生的。除了生成泛型的一般版本之外,使用Type Specialization還可以讓編譯器爲基本類型生成專門的版本。
語法
class Vector[@specialized A] { def apply(i:Int): A = //... def map[@specialized(Int, Boolean) B](f: A=>B) = //... } |
上面的代碼中Vector會將A爲每一個基本類型生成一個專門的版本,而方法map僅僅會生成Int和Boolean的對應版本。
實現
Type Specialization是在定義階段執行的。編譯器爲每一個基本類型或者用戶指定的類型生成對應的版本。Type Specialization在定義階段執行可以允許分塊編譯。如果基本類型的專門化在泛型類初始化的時候再執行的話,就有可能導致原始的類定義無法被同時編譯並且編譯器無法將方法專門化。
class RefCell[@specialized(Int) T] {
private var value: T = _
def get: T = x
def put(x: T) =
value = x
}
|
Scalac會生成一個額外的類繼承於RefCell[Int],這個類裏包含了所有RefCell成員的特殊化版本。它重寫了所有的泛型成員定義來實現它們的專門化版本。這個代碼路徑不但包括了自動裝箱和類型擦除,同時還必須保證程序在通過泛型接口調用它時,得到的結果是正確的。當在上下文中使用泛型代碼並且有更多的類型信息可用,同時還存在專門化的版本時,編譯器就會將類的實例化和方法調用重寫成專門化的版本。這樣的代碼路徑可以保證避免自動裝箱:
class RefCell$mcI$sp extends RefCell/*[Int]*/ {
protected var value$mcI$sp: Int = _;
protected override def value: AnyRef =
value$mcI$ // boxing happens here
protected override def value_=(x$1: Int): Unit =
value$mcI$sp = x$1
override def get: AnyRef =
get$mcI$sp; // boxing happens here
override def get$mcI$sp: Int = value$mcI$sp;
override def put(x: AnyRef): Unit =
put$mcI$sp(x); // boxing happens here
override def put$mcI$sp(x: Int): Unit = value$mcI$sp = x
}
|
上面的這個類就是爲類型Int專門化的RefCell類。它包含一個專門化後的整型字段來保存當前的值,重寫了繼承字段的訪問方法,並且用新的訪問方法代替原有的訪問方法。除了字段以外,涉及到泛型的方法也會被重寫。實現的方式就是利用專門化的類型來替代泛型T的引用並且儘可能的使用專門化後的字段和方法。
假設我們的代碼需要使用RefCell來處理Int類型:
object Test extends Application {
val ref = new RefCell[Int]
ref.put(10)
println(ref.get)
}
|
Scala的編譯器會使用專門化的版本將RefCell[Int]的實例替換掉,這樣在put和get的時候就不會發生自動裝箱拆箱了。
成員專門化
這裏需要注意的是,並不是所有的成員都會被專門化。假設我們有一個成員變量m,通常情況下,m至少應該是一個純的專門化類型,或者是專門化類型的數組,這樣纔會被編譯器專門化。例如:
abstract class Foo[@specialized T, U] {
// the following members are specialized
def foo1(x: T): U
def foo2(x: Int): Array[T]
val a: Array[T]
// the following members are not specialized
def bar1(x: U): Unit
def bar2(x: List[T]): U
val b: List[T]
}
|
當前Type Specialization的狀態
標準庫中的Type Specialization
在Scala的標準庫中,以下的class使用了Type Specialization:- 兩參數的FunctionN trait。參數的類型被專門化爲Int, Long, Float和Double。結果的類型參數還加入了Unit和Boolean的專門化。
- AbstractFunctionN和上面的FunctionN一樣也加入了相同的專門化。
- 兩參數的Tuple被專門化爲Int, Long和Double。
- 方法Range.foreach被專門化爲Unit,這樣可以加快整數的for循環:for(i<-0 until 100) //...