首页 > 代码库 > 探索Scala(6)-- Tuples

探索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方法。


探索Scala(6)-- Tuples