本节翻译自
综述:本节介绍了函数中默认参数和带名参数;操作符以及标识符的优先级,还有可以从输入中提取元组或值的序列的提取器。
默认参数
Scala提供了给参数默认值的能力,可以让调用者忽略这些参数。
def log(message: String, level: String = "INFO") = println(s"$level: $message")
log("System starting") // prints INFO: System starting
log("User not found", "WARNING") // prints WARNING: User not found
参数 level
有一个默认值,所以它是可选的。在最后一行,参数 "WARNING"
覆盖默认参数 "INFO"
。你可能在Java中使用重载方法,可以使用带有可选参数的方法来实现相同的效果。但是,如果调用者省略了一个参数,那么必须指定任何下列参数。
class Point(val x: Double = 0, val y: Double = 0)
val point1 = new Point(y = 1)
这里我们必须要指明 y = 1
。
注意,当从 Java 代码调用时,Scala中的默认参数不是可选的:
// Point.scala
class Point(val x: Double = 0, val y: Double = 0)
// Main.java
public class Main {
public static void main(String[] args) {
Point point = new Point(1); // does not compile
}
}
带名参数
在调用方法时,您可以使用参数名称来标记参数:
def printName(first: String, last: String): Unit = {
println(first + " " + last)
}
printName("John", "Smith") // Prints "John Smith"
printName(first = "John", last = "Smith") // Prints "John Smith"
printName(last = "Smith", first = "John") // Prints "John Smith"
注意命名参数的顺序可以重新排列。但是,如果有一些参数被命名,而另一些参数没有,则未命名的参数必须以方法签名中参数的顺序排在第一位。
def printName(first: String, last: String): Unit = {
println(first + " " + last)
}
printName(last = "Smith", "john") // Does not compile
请注意,带名参数与对Java方法的调用不兼容。
操作符
在 Scala 中,操作符是一个方法。任何带有单个参数的方法都可以使用中缀操作符。例如,+
可以用点符号来表示:
10.+(1)
然而,作为一个中缀操作符更容易阅读:
10 + 1
定义和使用操作符
你可以使用任何合法的标识符作为操作符。这包括 add
或像 +
符号之类的名称。
case class Vec(val x: Double, val y: Double) {
def +(that: Vec) = new Vec(this.x + that.x, this.y + that.y)
}
val vector1 = Vec(1.0, 1.0)
val vector2 = Vec(2.0, 2.0)
val vector3 = vector1 + vector2
vector3.x // 3.0
vector3.y // 3.0
类 Vec
有一个方法 +
,我们使用它来把 vector1
和 vector2
加起来。使用圆括号的话,你就可以使用可读的语法构建复杂的表达式。下面是类 MyBool
的定义,它包括了 and
和 or
方法:
case class MyBool(x: Boolean) {
def and(that: MyBool): MyBool = if (x) that else this
def or(that: MyBool): MyBool = if (x) this else that
def negate: MyBool = MyBool(!x)
}
现在可以使用将 and
和 or
方法作为中缀操作符来使用了:
def not(x: MyBool) = x.negate
def xor(x: MyBool, y: MyBool) = (x or y) and not(x and y)
这有助于使 xor
的定义更加可读。
优先级
当一个表达式使用多个操作符时,操作符会根据第一个字符的优先级进行计算。
(characters not shown below)
* / %
+ -
:
= !
< >
&
^
|
(all letters)
这适用于你定义的函数。例如,下面的表达式:
a + b ^? c ?^ d less a ==> b | c
相似于
((a + b) ^? (c ?^ d)) less ((a ==> b) | c)
?^
的优先级最高,因为它以字符 ?
开头。+
具有第二优先级,其次是 ^?
、==>
、|
和 less
。
提取器对象
提取器对象是具有一个 unapply
方法的对象。与其相反的是 apply
方法就像一个构造函数,它接受参数并创建一个对象,而 unapply
则获取一个对象并试图返回参数。这通常用于模式匹配和部分应用函数中。
import scala.util.Random
object CustomerID {
def apply(name: String) = s"$name--${Random.nextLong}"
def unapply(customerID: String): Option[String] = {
val name = customerID.split("--").head
if (name.nonEmpty) Some(name) else None
}
}
val customer1ID = CustomerID("Sukyoung") // Sukyoung--23098234908
customer1ID match {
case CustomerID(name) => println(name) // prints Sukyoung
case _ => println("Could not extract a CustomerID")
}
apply
方法从一个 name
创建一个 CustomerID
字符串。而 unapply
反过来将 name
提取出来。我们调用的 CustomerID("Sukyoung")
,其实是 CustomerID.apply("Sukyoung")
的简写语法。当我们调用 case CustomerID(name) => customer1ID
时,实际上我们使用的是 unapply
方法。
unapply
方法也可以用来赋值。
val customer2ID = CustomerID("Nico")
val CustomerID(name) = customer2ID
println(name) // prints Nico
这相当于 val name = CustomerID.unapply(customer2ID).get
,如果没有使用匹配,会抛出 scala.MatchError
:
val CustomerID(name2) = "--asdfasdfasdf"
unapply
的返回类型应该选择如下几种:
- 如果只是一个测试,返回一个布尔值。例如
case even()
- 如果它返回T类型的一个子值,返回一个
Option[T]
- 如果你想返回几个子值
T1,...,Tn
,将它们分组为一个可选的元组Option[(T1,...,Tn)]
。
有时,子值的数量不是固定的,我们希望返回一个序列。出于这个原因,你还可以通过 unapplySeq
定义模式,该机制返回 Option[Seq[T]]
,该机制用于模式 case List(x1, ..., xn)
。