Scala 下划线(_)的多种应用场景学习

[color=red][b]1、作为“通配符”,类似Java中的*。如import scala.math._[/b][/color]

[color=blue][b]2、:_*作为一个整体,[/b][/color]告诉编译器你希望将某个参数当作参数序列处理!例如val s = sum(1 to 5:_*)就是将1 to 5当作参数序列处理。

3、指代一个集合中的每个元素。例如我们要在一个Array a中筛出偶数,并乘以2,可以用以下办法:a.filter(_%2==0).map(2*_)。又如要对缓冲数组ArrayBuffer b排序,可以这样:val bSorted = b.sorted(_)

4、在元组中,可以用方法_1, _2, _3访问组员。如a._2。其中句点可以用空格替代。

5、使用模式匹配可以用来获取元组的组员,例如val (first, second, third) = t但如果不是所有的部件都需要,那么可以在不需要的部件位置上使用_。比如上一例中val (first, second, _) = t

[size=medium][color=red][b]6、还有一点,下划线_代表的是某一类型的默认值。对于Int来说,它是0。对于Double来说,它是0.0对于引用类型,它是null。[/b][/color][/size]


下划线这个符号几乎贯穿了任何一本Scala编程书籍,并且在不同的场景下具有不同的含义,绕晕了不少初学者。正因如此,[color=red][b]下划线这个特殊符号无形中增加Scala的入门难度。[/b][/color]本文希望帮助初学者踏平这个小山坡。

[size=x-large][color=red][b]1. 用于替换Java的等价语法[/b][/color][/size]

由于大部分的Java关键字在Scala中拥有了新的含义,所以一些基本的语法在Scala中稍有变化。

[b]1.1 导入通配符[/b]

*在Scala中是合法的方法名,所以导入包时要使用_代替。

//Java
import java.util.*;

//Scala
import java.util._


[b]1.2 类成员默认值[/b]

Java中类成员可以不赋初始值,编译器会自动帮你设置一个合适的初始值:

class Foo{
//String类型的默认值为null
String s;
}

[size=large][color=blue][b]而在Scala中必须要显式指定,如果你比较懒,可以用_让编译器自动帮你设置初始值:[/b][/color][/size]

class Foo{
//String类型的默认值为null
var s: String = _
}

[size=medium][color=red][b]该语法只适用于类成员,而不适用于局部变量。[/b][/color][/size]

[size=large][color=red][b]1.3 可变参数[/b][/color][/size]

Java声明可变参数如下:

public static void printArgs(String ... args){
for(Object elem: args){
System.out.println(elem + " ");
}
}

调用方法如下:

//传入两个参数
printArgs("a", "b");
//也可以传入一个数组
printArgs(new String[]{"a", "b"});

在Java中可以直接将数组传给printArgs方法,[color=red][b]但是在Scala中,你必须要明确的告诉编译器,你是想将集合作为一个独立的参数传进去,还是想将集合的元素传进去。[/b][/color]如果是后者则要借助下划线:

printArgs(List("a", "b"): _*)


[color=red][b]1.4 类型通配符[/b][/color]
Java的泛型系统有一个通配符类型,例如List<?>,任意的List<T>类型都是List<?>的子类型,如果我们想编写一个可以打印所有List类型元素的方法,可以如下声明:
public static void printList(List<?> list){
for(Object elem: list){
System.out.println(elem + " ");
}
}

[size=medium][color=red][b]对应的Scala版本为:[/b][/color][/size]

def printList(list: List[_]): Unit ={
list.foreach(elem => println(elem + " "))
}


[color=red][size=large][b]2 模式匹配[/b][/size][/color]

[b]2.1 默认匹配[/b]

str match{
case "1" => println("match 1")
case _ => println("match default")
}
[b]2.2 匹配集合元素[/b]

//匹配以0开头,长度为三的列表
expr match {
case List(0, _, _) => println("found it")
case _ =>
}

//匹配以0开头,长度任意的列表
expr match {
case List(0, _*) => println("found it")
case _ =>
}

//匹配元组元素
expr match {
case (0, _) => println("found it")
case _ =>
}

//将首元素赋值给head变量
val List(head, _*) = List("a")


[size=large][b]3. Scala特有语法[/b][/size]

[b]3.1 访问Tuple元素
[/b]
val t = (1, 2, 3)
println(t._1, t._2, t._3)


[b]3.2 简写函数字面量(function literal)[/b]

如果函数的参数在函数体内只出现一次,则可以使用下划线代替:

val f1 = (_: Int) + (_: Int)
//等价于
val f2 = (x: Int, y: Int) => x + y

list.foreach(println(_))
//等价于
list.foreach(e => println(e))

list.filter(_ > 0)
//等价于
list.filter(x => x > 0)


[size=medium][b]3.3 定义一元操作符[/b][/size]

在Scala中,操作符其实就是方法,例如1 + 1等价于1.+(1),利用下划线我们可以定义自己的左置操作符,例如Scala中的负数就是用左置操作符实现的:

-2
//等价于
2.unary_-
[b]3.4 定义赋值操作符[/b]

我们通过下划线实现赋值操作符,从而可以精确地控制赋值过程:

class Foo {
def name = { "foo" }
def name_=(str: String) {
println("set name " + str)
}

val m = new Foo()
m.name = "Foo" //等价于: m.name_=("Foo")

[color=red][b]3.5 定义部分应用函数(partially applied function)[/b][/color]

我们可以为某个函数只提供部分参数进行调用,返回的结果是一个新的函数,即部分应用函数。因为只提供了部分参数,所以部分应用函数也因此而得名。

def sum(a: Int, b: Int, c: Int) = a + b + c
val b = sum(1, _: Int, 3)
b: Int => Int = <function1>
b(2) //6


[color=red][b]3.6 将方法转换成函数[/b][/color]

[color=red][b]Scala中方法和函数是两个不同的概念,方法无法作为参数进行传递,也无法赋值给变量,但是函数是可以的。[/b][/color]在Scala中,利用下划线可以将方法转换成函数:

//将println方法转换成函数,并赋值给p
val p = println _  
//p: (Any) => Unit


转自:[url]https://my.oschina.net/joymufeng/blog/863823[/url]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值