位置:首页 > Scala > Scala 下划线 `_` 的常见用法,一次看清它在不同语境里的含义

Scala 下划线 `_` 的常见用法,一次看清它在不同语境里的含义

时间:2026-08-25  |  作者:游戏探长  |  阅读:0

目录

  1. 成员变量初始化:用 `_` 取得类型默认值
  2. 方法、函数与偏应用:下划线如何参与函数化
  3. 导包与高阶函数:最常见也最容易顺手写的两类场景
  4. 元组、可变参数和模式匹配:三种经常一起考察的语法点
  5. 命名约定中的下划线:手写 setter 的实现方式
  6. 怎么快速判断当前的 `_` 到底是什么意思

前言

Scala 的下划线 `_` 看起来只是一个符号,实际却横跨变量初始化、函数式写法、模式匹配和命名约定等多种语法场景。很多人不是不会写,而是容易在不同上下文里混淆语义。本文按开发中最常见的使用位置重组示例,帮助你快速判断它当前代表的是默认值、参数占位,还是函数转换与语法展开。

在 Scala 里,下划线 _ 几乎无处不在,看起来像一个统一符号,实际却承担了默认值、占位参数、方法转函数、可变参数展开等多种角色。真正难的不是记住它出现过,而是判断当前语境里它到底代表什么、哪些写法能省、哪些地方又必须补上类型说明。下面按常见开发场景逐项梳理,结合原始代码示例,把容易混淆的几种用法一次看清。

成员变量初始化:用 _ 取得类型默认值

Scala 和 Java 一样,类中的成员变量如果没有显式赋值,可以拥有默认值。但在 Scala 里,如果你想手动声明“这里就用默认值”,需要写成 = _。这一写法只适用于 var,不能直接用于 val

这里要特别注意一点:如果希望某个引用类型最终表现为 null,就必须显式写出变量类型;否则编译器会把它推断成 Null,语义就变了。

// _ 对应的默认值:整型默认值0;浮点型默认值0.0;String与引用类型,默认值null; Boolean默认值false
class Student{
    //String类型的默认值为null
    var name : String = _
    var age: Int = _
    var amount: Double = _
    var mOrF: Boolean = _
}

可以把这类用法理解为“按类型补零值”:Int0Double0.0Booleanfalse,字符串和其他引用类型通常是 null

方法、函数与偏应用:下划线如何参与函数化

Scala 里经常把 def 定义的方法和 val 保存的函数放在一起使用。多数时候两者可以顺畅配合,但在某些场合,方法并不会自动当成函数值处理,这时就需要借助下划线完成转换。

Scala 下划线在函数相关场景中的用法对比信息图
下划线在函数场景里的三种角色把方法转函数、偏应用和匿名参数占位放在一张图里,更容易看出它们虽然都写下划线,但语义并不相同。

方法转函数

严格说,def 定义的是方法,val 接住的是函数值。把方法转成函数时,常见写法就是在方法名后补一个 _

scala> def f1 = ()=>{}
scala> val f2 = f1 _ 

这类写法的核心含义不是“执行方法”,而是“把方法本身交出去”。当你需要把一个方法赋值给变量、作为参数传给高阶函数,或者延后调用时,这种转换就很常见。

部分应用函数与参数占位

下划线在偏应用函数里也很重要。所谓部分应用函数,就是一个原本有多个参数的函数,只先固定其中一部分,剩余参数留待后续调用时再传入。这个“留空位”的动作,通常就由 _ 来表示。

// 定义一个函数
def add(x:Int, y:Int, z:Int) = x+y+z
// Int不能省略
def addX = add(1, _:Int, _:Int)
addX(2,3)
addX(3,4)
def addXAndY = add(10, 100, _:Int)
addXAndY(1)
def addZ = add(_:Int, _:Int, 10)
addZ(1,2)
// 省略了全部的参数,下面两个等价。第二个更常用
def add1 = add(_: Int, _: Int, _: Int)
def add2 = add _ 

这里的重点有两个。第一,参数类型在某些位置不能省,尤其是编译器无法准确推断时,_ : Int 这种写法要保留。第二,如果全部参数都交给下划线处理,那么 add _ 就是更常见也更简洁的写法。

导包与高阶函数:最常见也最容易顺手写的两类场景

很多开发者第一次熟悉下划线,往往就是从导包和集合操作开始的。这两种场景出现频率高,而且写法很短,但它们表示的含义并不相同。

导入包内全部成员

在 Scala 中,导入包下全部内容的写法是 _,作用类似 Java 里的 *

//Scala
import ja va.util._
//Ja va
import ja va.util.*; 

这类语法比较直观,记忆成本不高,关键是不要和其他场景里的“参数占位”混在一起理解。

高阶函数里省去参数名

mapcountsortWithfilterreduce 这类高阶函数中,传入的往往只是一个很短的匿名函数。如果参数名本身没有语义价值,就可以直接用 _ 来占位,减少样板代码。

val list = List(3,3,5)
list.reduce(_+_) //等同于list.sum()
list.map(_ * 2)
list.filter(_ > 3) 

这类写法适合逻辑非常直接的场景,比如“每个元素乘 2”“只保留大于 3 的元素”。但如果表达式一长,或者包含多个条件判断,继续强行使用下划线反而会影响可读性,这时恢复成显式参数名更稳妥。

元组、可变参数和模式匹配:三种经常一起考察的语法点

除了函数式风格中的大量使用,下划线还常出现在 Scala 的基础语法里。它们看起来分散,但都属于“用一个符号代替具体名字或结构”的思路。

Scala 下划线在基础语法中的位置型与展开型用法信息图
位置不同,下划线含义也不同元组访问、可变参数展开和模式匹配经常一起出现,这张图用于区分它们各自对应的语法位置。

访问元组元素

元组中的元素可以通过 _1_2 这类方式访问。

val tu = (1,2,3)
tu._1
tu._2 

它不是匿名占位,而是元组位置访问器。看到 _1_2 时,应当直接理解为“第 1 个元素”“第 2 个元素”。

集合展开为多个参数

如果方法接收的是可变参数,而你手里已经有一个数组或集合,那么可以通过 _:* 把集合展开成多个独立参数。

def addSum(nums: Int*) = {
  nums.sum
}

addSum(1 to 10: _*))

这个写法在调用可变参数方法时很实用,尤其是数据本来就来自 ListRange、数组等集合类型时,可以避免手动拆参数。

模式匹配中的默认项与类型占位

在模式匹配里,下划线通常表示“兜底分支”或“这个位置的名字不重要”。做类型匹配时,也可以只写类型,不再额外起变量名。

val a = 10
a match {
  case _: Int => println("Int")
  case _ => println("defalult")
} 

这里的 case _: Int 表示“只关心它是不是 Int,不关心变量名”,而 case _ 则是标准的默认分支。

命名约定中的下划线:手写 setter 的实现方式

Scala 里下划线还会出现在成员变量和 setter 方法的命名中。这种写法常用于手动封装内部状态,并暴露一组更符合 Scala 风格的访问方法。

下面这个例子中,_leg 是私有字段,leg 是 getter,leg_= 则是 setter。调用时既可以显式写成 dog.leg_=(4),也可以直接写成更自然的 dog.leg = 5

class Dog {
  private var _leg = 0
  def leg: Int = _leg
  def leg_=(newLag: Int) = {
    _leg = newLag
  }

  def get() = {
    _leg
  }
}
object GetterAndSettre {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.leg_=(4) //等同于 dog.leg = 4 ,都是修改了_leg的值
    println(dog.get())
    dog.leg = 5
    println(dog.get())

  }

}

这类下划线更多是一种命名和访问约定。前缀下划线强调内部字段,后缀 _= 则让赋值语法能够按 Scala 的方式工作起来。

怎么快速判断当前的 _ 到底是什么意思

理解 Scala 下划线,最有效的方法不是死记“九种用法”,而是先看它所在的语境:

  • 出现在 = _ 中,通常表示按类型取默认值;
  • 出现在方法名后面,多半是在把方法转换成函数;
  • 出现在高阶函数参数位置,通常是匿名参数占位;
  • 出现在 _:* 中,表示把集合展开成可变参数;
  • 出现在 _1_2 中,表示访问元组位置;
  • 出现在 case _case _: Type 中,属于模式匹配语法;
  • 出现在 leg_= 这类名字里,则是 setter 命名规则的一部分。

从开发角度看,下划线确实能大幅减少样板代码,但前提是场景合适、可读性还在。写得太“省”,有时反而会把语义藏起来。真正实用的经验是:能一眼看懂就用,下划线一多开始费解时,就把参数名和类型补回来。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多