探索Scala(5)-- 基本類型

文本討論一下Scala語言基本類型的實現方式

Java基本類型

Java的數據類型大致可以分爲兩類:基本(Primitive)類型和對象類型。基本類型一共有8種,分別是boolean、byte、short、char、int、long、float、double。爲了把基本類型融入OO系統,Java提供了包裝類(Wrapper)。包裝類在包java.lang裏,一共8個,分別與基本類型一一對應,它們是:Boolean、Byte、Short、Character、Integer、Long、Float和Double。Java5引入的自動裝箱拆箱(Autoboxing and Unboxing)語法讓基本類型和OO系統結合的更自然,但仍然不夠完美。

Scala基本類型

Scala在Java的基礎上邁出了一大步,從語法層面徹底消滅了Primitive類型。也就是說,一切皆是對象,如下面代碼所示:

val x = 1
println(x.toString)

3.to(8).foreach {
    i => println(i)
}

和Java的8種基本類型相對應,Scala也定義了8種基本類型,它們是:Boolean、Byte、Short、Char、Int、Long、Float和Double。這8種基本類型都定義在scala包裏。有趣的是,這8種基本類型雖然有相應的類定義,但是和其他類還是有區別的:這些類的實例並不是對象,而是直接映射爲相應的primitive類型。比如,下面是Int類的部分代碼:

package scala

/** `Int`, a 32-bit signed integer (equivalent to Java's `int` primitive type) is a
 * subtype of [[scala.AnyVal]]. Instances of `Int` are not
 * represented by an object in the underlying runtime system.
 *
 * There is an implicit conversion from [[scala.Int]] => [[scala.runtime.RichInt]]
 * which provides useful non-primitive operations.
 */
final abstract class Int private extends AnyVal {
    ...
}

底層使用Primitive類型

雖然從語法上來看,基本類型和對象類型已經沒有任何區別。但是在底層,Scala使用的仍然是primitive類型。比如下面這個方法:

def collect(x: Int, y: Int): Array[Int] = {
    Array(x, y + 1)
}
經過Scala編譯之後再反編譯,可以得到下面的Java代碼:

public int[] collect(int x, int y) {
    return new int[] {x, y + 1};
}

包裝爲Java包裝類型

必要時,Scala會把基本類型包裝成相應的Java包裝類型,比如下面這個方法:

def boxToXXX() = {
    val x = 1
    val str = x.toString()
    val hc = x.hashCode()
}
反編譯得到下面的Java代碼:

int x = 1;
Integer boxedX = scala.runtime.BoxesRuntime.boxToInteger(x);
boxedX.toString();
boxedX.hashCode();
BoxesRuntime類的boxToInteger()方法調用了Integer的valueOf()方法,如下所示:

public static java.lang.Integer boxToInteger(int i) {
    return java.lang.Integer.valueOf(i);
}

隱式轉換爲Java包裝類型

基本類型也可以通過隱式轉換變爲Java包裝類型,比如下面這個方法:

def implicitConversion() = {
    val x = 1
    val y = 2
    x.compareTo(y)
}
反編譯之後,得到下面的Java代碼:

int x = 1;
int y = 2;
Integer boxedX = scala.Predef$.MODULE$.int2Integer(x);
Integer boxedY = scala.Predef$.MODULE$.int2Integer(y);
boxedX.compareTo(boxedY);
隱式轉換方法定義在Predef裏,下面是Predef的相應代碼:

object Predef {
    implicit def int2Integer(x: Int) = java.lang.Integer.valueOf(x)
}

隱式轉換爲RichXXX

爲了給基本類型添加更多的方法,Scala給每個基本類型都定義了一個新的包裝類,這些包裝類在scala.runtime包裏,分別是:RichBoolean、RichByte、RichShort、RichChar、RichInt、RichLong、RichFloat和RichDouble。比如下面這個方法:

def richXXX() = {
    val x = -1
    x.abs
}
反編譯之後,得到下面的Java代碼:

int x = -1;
RichInt richX = scala.Predef$.MODULE$.intWrapper(x);
richX.abs();
下面是Predef的相應代碼:

object Predef extends LowPriorityImplicits {...}
private[scala] abstract class LowPriorityImplicits {
    @inline implicit def intWrapper(x: Int) = new runtime.RichInt(x)
}

隱式轉換爲BigXXX

爲了支持任意精度的整數和小數,Java提供了BigIntegerBigDecimal類,這兩個類在java.math包裏。Scala在scala.math包裏提供了兩個類似的類:BigIntBigDecimal。這兩個類都重載了各種運算符,所以使用起來更加自然:

def bigXXX() = {
    val x: BigInt = 237
    val y = (x * 7) pow 95
}
下面是反編譯之後的bigXXX方法:

int x = 237;
BigInt bigX = BigInt$.MODULE$.int2bigInt(x);
BigInt y = bigX.$times(7).pow(95);
下面是BigInt相應代碼:

object BigInt {
    implicit def int2bigInt(i: Int): BigInt = apply(i)
}
final class BigInt(val bigInteger: BigInteger) {
    def + (that: BigInt): BigInt = new BigInt(this.bigInteger.add(that.bigInteger))
    def - (that: BigInt): BigInt = new BigInt(this.bigInteger.subtract(that.bigInteger))
    def * (that: BigInt): BigInt = new BigInt(this.bigInteger.multiply(that.bigInteger))
    def / (that: BigInt): BigInt = new BigInt(this.bigInteger.divide(that.bigInteger))
    def pow (exp: Int): BigInt = new BigInt(this.bigInteger.pow(exp))
}
可見BigInt只是簡單的包裝Java的BigInteger而已,而且還提供了一個單例對象。爲了提高效率,BigInt還採用了和java.lang,Integer類似的緩存優化,把-1024到1024之間的數緩存了起來:

object BigInt {
    private val minCached = -1024
    private val maxCached = 1024
    private val cache = new Array[BigInt](maxCached - minCached + 1)

    def apply(i: Int): BigInt =
        if (minCached <= i && i <= maxCached) {
            val offset = i - minCached
            var n = cache(offset)
            if (n eq null) { n = new BigInt(BigInteger.valueOf(i.toLong)); cache(offset) = n }
            n
        } else new BigInt(BigInteger.valueOf(i.toLong))
}

參考資料

Scala API

Scala源代碼

《Programming in Scala》第二版

《Scala for the Impatient》


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章