本文討論一下Tuple的用法和實現方式
Unit
Scala語言沒有void關鍵字,取而代之的,是Unit概念(和對象)。Scala比Java更加OO,這也算是其中一個方面。從Scala語言的角度來講,Unit和Tuple並沒有太大的聯繫,但是基於兩點原因,我打算先討論一下Unit:
- 概念相似:Tuple表示包含n(n > 0)個元素的對象,Unit表示沒有對象,或不需要對象。所以Unit可以認爲是Tuple的一種特殊情況,即包含0個元素的Tuple。
- 語法相似:可以用圓括號來創建Tuple實例,比如,val t = (1, "2", false)創建了一個三元組。一對兒空圓括號則創建Unit實例,比如val u = ()
大部分時候,Unit都只是個概念,它等價於Java的void關鍵字。比如下面這段Scala代碼:
def unitIsVoid(x: Int, y: Int): Unit = {
val z = x + y
}
反編譯之後,實際上得到下面的Java代碼:
public void unitIsVoid(int x, int y) {
int z = x + y;
}
但如果你非要使用Unit實例的話,也是行得通的:
def unitIsNotVoid(nothing: Unit) = {
val unit = ()
if (unit == nothing) {
println("unit == nothing")
}
}
上面的Scala代碼可以通過編譯,但是編譯器會給出一條警告:
warning: comparing values of types Unit and Unit using `==' will always yield true
下面是反編譯之後的Java代碼:
public void unitIsNotVoid(scala.runtime.BoxedUnit nothing) {
scala.runtime.BoxedUnit unit = BoxedUnit.UNIT;
if (unit.equals(nothing)) {
...
}
}
可以看到,Unit概念的化身,就是BoxedUnit.UNIT單例對象。
Tuple1
Tuple1就是隻包含一個元素的Tuple,也就是一元組。需要注意的是,Tuple1是無法用圓括號字面量來實例化的,必須用Tuple1("x")這樣的方式來實例化,如下面代碼所示:
def tuple1() = {
val x = ("a")
val y = Tuple1("b")
}
下面是反編譯之後的Java代碼:
String x = "a";
Tuple1 y = new Tuple1("b");
TupleN
那麼可以任意表達包含n個元素的Tuple嗎?答案是否定的。Scala只定義了Tuple1~Tuple22共22個Tuple(在scala包裏),圓括號字面量也只支持Tuple1~Tuple22,再多就不支持了。例如下面這段代碼是無法通過編譯的:
def tuple23() = {
val t23 = (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,"21","22","23")
}
Scala編譯器報了下面這個錯誤:
error: object <none> is not a member of package scala
函數返回多個值
Tuple有很多用途。當方法想返回多個值,但又不太值得單獨定義一個類的時候,就比較適合用Tuple。下面是一個例子:
def nameAndAge() = {
("zxh", 32)
}
把Tuple賦值給多個變量
也可以把Tuple賦值給n個變量,這相當於把Tuple給拆了,代碼如下所示:
def assignToVars() = {
val (name, age) = nameAndAge()
println(name + " is " + age + " old")
}
實例化Map
下面這段代碼實例化了一個Map:
val m = Map("a" -> 1, "b" -> 2)
看起來Scala好像支持Map字面量,但實際上並不是。下面是反編譯之後的Java代碼:
Tuple2[] ts = new Tuple2[2];
ts[0] = scala.Predef$ArrowAssoc$.MODULE$.$minus$greater$extension("a", 1); // ->
ts[0] = scala.Predef$ArrowAssoc$.MODULE$.$minus$greater$extension("b", 2);
scala.collection.immutable.Map$.apply(ts);
->實際上只是個隱式轉換方法,下面是它的定義:
object Predef {
implicit final class ArrowAssoc[A](private val self: A) extends AnyVal {
@inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
}
}
也就是說,->把key和value轉換成了Tuple2,然後Tuple2數組傳遞給了Map單例對象的apply方法。