探索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提供了BigInteger和BigDecimal類,這兩個類在java.math包裡。Scala在scala.math包裡提供了兩個類似的類:BigInt和BigDecimal。這兩個類都過載了各種運算子,所以使用起來更加自然:
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))
}
參考資料
《Programming in Scala》第二版
相關文章
- [譯] Scala 型別的型別(四)型別
- [譯] Scala 型別的型別(二)型別
- [譯] Scala 型別的型別(三)型別
- [譯] Scala 型別的型別(六)型別
- [譯] Scala 型別的型別(五)型別
- Scala 泛型型別和方法泛型型別
- javascript基本型別 引用型別 基本包裝型別JavaScript型別
- Scala(一)資料型別資料型別
- 【Scala之旅】型別引數型別
- Scala結構型別與複合型別解析型別
- Typescript:基本型別TypeScript型別
- Go 基本型別Go型別
- Redis 的 5 種資料型別的基本使用Redis資料型別
- js基本型別和引用型別區別JS型別
- JAVA 基本型別與 引用型別區別Java型別
- JavaScript - 基本型別與引用型別值JavaScript型別
- Java的基本型別和引用型別Java型別
- 基本資料型別與字串型別資料型別字串
- JavaScript筆記5:計時器、物件、基本資料型別、引用資料型別JavaScript筆記物件資料型別
- Go語言5大基本資料型別解析Go資料型別
- 探索Scala(2)-- TraitsAI
- 【Scala之旅】特質與高階型別型別
- scala和java資料型別轉換Java資料型別
- JavaFX教程——基本型別Java型別
- 基本資料型別資料型別
- JS篇-基本型別和引用型別、typeofJS型別
- JS基本型別與引用型別知多少JS型別
- 你不知道的JavaScript--Item4 基本型別和基本包裝型別(引用型別)JavaScript型別
- Java基礎-基本型別和包裝型別Java型別
- Scala 中的集合(一):集合型別與操作型別
- Scala型別類的小應用之Functor Foldable型別LDA
- 探索Scala(3)-- 單例物件單例物件
- JavaScript基本資料型別JavaScript資料型別
- Java 基本資料型別Java資料型別
- 原子更新基本型別類型別
- JavaScript基本型別總結JavaScript型別
- Redis基本資料型別Redis資料型別
- Java -基本資料型別Java資料型別