Scala中的_
echohlne

Scala 中的 _ 是一个功能强大且灵活的语法糖,在不同的场景中有不同 的含义,总结记录一下目前碰到的 _ 的含义

模块导入

Scala 中导入 package 时, _ 类似于 Java导包的 *

1
2
3
4
5
6
7
8
9
// 引入 spark.sql 包中的所有类
import org.apache.spark.sql._
// 引入对象 SparkSession 中的所有成员(相当于 Java 中的 static import)
import org.apache.spark.sql.SparkSession._
// 引入对象 SParkSession 中的所有成员, 但是将 SparkSession 改名为 CustomSparkSession , 相当于 Python 中的 `import Foo from com.test.Fun as Bar`
import org.apache.spark.sql.{SparkSession => CustomSparkSesion, _}
// 引入 spark.sql 中的所有成员,但是通过将 SparkSession 改名为 _ 而忽略
import org.apache.spark.sql.{SparkSession => _, _}

初始化赋值

_ 用于一个类的初始化赋值。 注意, 如果成员声明在构造器参数列表时, 则不能使用 _赋值。

1
2
3
4
5
// beforeConverted 必须显式赋值
// 成员 afterConverted 可以通过 _ 赋值
case class Converter(beforeConverted:String) {
var afterConverted:String = _
}

模式匹配

Scala中的模式匹配和 C/C++ 或者 Java中的 switch-case 语句类似。 在 Scala 中的模式匹配中, 下划线 _ 是匹配任意内容的通配符。最基本的用法中, _ 相当于 switch 中的 default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class Storage;

case class MemoryOnly() extends Storage;

case class DiskOnly() extends Storage;

case class MemoryWithSerialization() extends Storage;

case class DiskWithSerialization() extends Storage;


def getStorageType(storage: Storage): String = storage match {
case memoryOnly: MemoryOnly => return "memoryOnly"
case diskOnly: DiskOnly => return "diskOnly"
case _ => return "serialization"
}

更高阶的用法中, _ 可以嵌套使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class SQLConnector;

case class MySQLConnector(ip: String, port: Int, userName: String, password: String) extends SQLConnector

case class HBaseConnector(zookeeperConnectString: String) extends SQLConnector

case class RedisConnector(redisHost: String, auth: String) extends SQLConnector

def getConnectorMessage(sqlConnector: SQLConnector): String = sqlConnector match {
case MySQLConnector(ip, _, userName, _) => s"We want to connect mysql in ip:$ip with userName:$userName"
case HBaseConnector(zookeeperConnectString) => s"We want to connect hbase under zookeeper server(s):$zookeeperConnectString"
case RedisConnector(redisHost, _) => s"We want to connect redis in ip:$redisHost"
}

省略匿名表达式参数

ScalaPython, C++ 等语言一样,也有匿名函数的设定。 _ 可以用来作为匿名函数的参数占位符, 但是对于每一个参数, 只能用 _ 占位一次。 例如,在 Scala 中, 1 + _ 相当于 Python 中的 lambda x : x + 1

尤其要注意的是, 在Scala中, _+_表示匿名函数接受2个参数, 函数返回值是两个参数之和,除此之外, Scala 代码中的 print(_) 相当于 x => print(x):

1
2
3
Range(0, 100).foreach(print(_))
// 相当于 (x, y) => x + y
Range(0, 100).reduce(_ + _)

传递不定参数

Scala不支持将数组直接传入到一个不定参参数中, 此时使用 _* 符号进行转化

1
2
3
4
5
def sum(ints:Int*):Int = ints.sum

val numbers = Range(1, 200)
// _* 表示 numbers 内的每一个元素
sum(numbers:_*)

防止函数意外调用

在 Scala 中,函数是一等公民,和普通变量一样可以赋值。但由于在 Scala 中函数调用时可省略括号,如果你打算将一个函数赋值给一个新的变量,则函数可能会被意外地调用而后将函数的返回值赋值。这种时候,我们需要在函数名之后加上 _ 来阻止函数调用。

1
2
3
4
5
6
7
class CertainService {
def processService(): Unit = {
// detail service
}

val serviceHandler = processService _
}

忽略类型参数

_ 可以用做忽略类型参数, 和 Java 中的 <?> 类似:

1
2
3
4
5
def getLengthOfList(rawList: Option[List[_]]):Int = {
rawList match {
case Some(list: List[_]) => list.length
case None => 0
}

Trait中的自身引用

若希望特质仅被某个满足条件的类所使用,就可以自身引用做限定,写法举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
trait Tool {
 //它表示这是一个专门用在 Computer 类的特质。
 _ : Computer =>
 //....
 override def activate(): Unit = {
   println("use tools.")
}
}

class Computer {
 def activate() : Unit = println("Start")
}

区分getter和setter

在 Scala 中,函数是一等公民,和普通变量一样可以赋值。但由于在 Scala 中函数调用时可省略括号,如果你打算将一个函数赋值给一个新的变量,则函数可能会被意外地调用而后将函数的返回值赋值。这种时候,我们需要在函数名之后加上 _ 来阻止函数调用

1
2
3
4
5
6
7
8
9
class CertainService {
private var item:Int = -1
def display: Int = item

def display_ = (record:Int) => {
require(record > 0)
item = record
}
}
 Comments