探索Scala(6)-- Tuples

本文討論一下Tuple的用法和實現方式

Unit

Scala語言沒有void關鍵字,取而代之的,是Unit概念(和對象)。Scala比Java更加OO,這也算是其中一個方面。從Scala語言的角度來講,Unit和Tuple並沒有太大的聯繫,但是基於兩點原因,我打算先討論一下Unit:

  1. 概念相似:Tuple表示包含n(n > 0)個元素的對象,Unit表示沒有對象,或不需要對象。所以Unit可以認爲是Tuple的一種特殊情況,即包含0個元素的Tuple。
  2. 語法相似:可以用圓括號來創建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方法。


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