3、Scala基础语法:变量与常量、基本数据类型、字符串插值、代码块与表达式

好,咱们今天聊聊Scala最基础的东西。说实话,很多初学者觉得语法嘛,背一背就行了。但我告诉你,这里面的门道可不少。我在大数据项目中见过太多因为变量声明不当引发的血案了。

3.1 变量与常量:val vs var

Scala里声明变量有两种方式:valvar。这俩的区别,说白了就是一个不能改,一个能改。

val name = "Alice"   // 常量,不可重新赋值
var age = 30         // 变量,可以重新赋值

name = "Bob"         // 编译错误!val不能重新赋值
age = 31             // 没问题,var可以改

我个人习惯是:能用val就用val。为什么?因为不可变性是函数式编程的核心思想之一。你想想看,在大数据场景下,数据量动辄上亿条,如果变量到处都能被修改,调试起来简直要命。

核心原则:优先使用val,只有确定需要改变时才用var。

我曾经在一个Spark作业里,看到一个同事把所有变量都声明成var。结果呢?代码跑着跑着,某个中间变量被意外修改了,整个计算结果全错。排查了整整两天才找到问题。从那以后,我对val的执念就更深了。

小技巧:Scala的类型推断很强大。你不需要每次都写类型,编译器会自动推断。比如 val x = 10,Scala知道x是Int。

3.2 基本数据类型

Scala的基本数据类型和Java很像,但又不完全一样。嗯,这里要注意,Scala里一切皆对象,包括数字。

类型 描述 示例
Byte 8位有符号整数 val b: Byte = 127
Short 16位有符号整数 val s: Short = 32767
Int 32位有符号整数 val i = 100
Long 64位有符号整数 val l = 100L
Float 32位浮点数 val f = 3.14f
Double 64位浮点数 val d = 3.14
Char 16位Unicode字符 val c = 'A'
Boolean 布尔值 val flag = true
String 字符串(实际上是java.lang.String) val s = "Hello"

你可能会问:为什么Int和Long的默认值不一样?其实在Scala里,所有类型都是类,它们有各自的方法。比如你可以直接写 1.toString(),这在Java里是做不到的。

避坑指南:我曾经在写Spark UDF时,忘记给Long类型加L后缀,结果数据被截断成了Int。排查了好久才发现是类型问题。记住:Long字面量一定要加L或l后缀。

3.3 字符串插值

字符串插值是Scala的一个亮点。说白了,就是让你在字符串里直接嵌入变量或表达式,不用像Java那样用加号拼接。

val name = "Alice"
val age = 30

// s插值器
println(s"$name is $age years old")
// 输出:Alice is 30 years old

// 还可以嵌入表达式
println(s"Next year, ${name} will be ${age + 1}")
// 输出:Next year, Alice will be 31

Scala提供了三种插值器:

  • s插值器:最常用,直接嵌入变量和表达式
  • f插值器:格式化字符串,类似C语言的printf
  • raw插值器:不处理转义字符
// f插值器示例
val height = 1.75
println(f"$name is $height%.2f meters tall")
// 输出:Alice is 1.75 meters tall

// raw插值器示例
println(raw"Hello\nWorld")   // 输出:Hello\nWorld(不换行)
println("Hello\nWorld")      // 输出:Hello(换行)World

个人经验:在写Spark SQL的字符串拼接时,我强烈推荐用s插值器。它比Java的String.format更直观,也比加号拼接更安全。我曾经用加号拼接时漏了一个空格,导致整个SQL语法错误,排查了半天。

3.4 代码块与表达式

在Scala里,代码块 {} 不仅仅是为了组织代码。它本身就是一个表达式,会返回最后一个表达式的值。

val result = {
  val x = 10
  val y = 20
  x + y   // 这个就是代码块的返回值
}
println(result)  // 输出:30

为什么会这样?因为Scala是面向表达式的语言。几乎所有的东西都是表达式,都有返回值。包括if、for、match等。

// if也是表达式
val max = if (a > b) a else b

// match也是表达式
val description = x match {
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}

我记得刚开始用Scala时,总觉得这种写法很奇怪。但用习惯了之后,你会发现代码变得非常简洁。比如在Spark的map操作里,你可以直接写一个代码块:

val rdd = spark.sparkContext.parallelize(Seq(1, 2, 3))
val result = rdd.map { num =>
  val squared = num * num
  squared + 1
}
// 每个元素先平方再加1

关键理解:代码块的最后一行就是返回值。不需要写return关键字。这是Scala的惯例,也是函数式编程的风格。

你想想看,这种设计有什么好处?它让代码更紧凑,减少了临时变量的声明。在大数据处理中,每一行代码都可能是性能瓶颈。少写几行,就少几个出错的机会。

避坑指南:我曾经在代码块里不小心加了一个空行,结果返回值变成了Unit。因为空行在Scala里会被解析成一个空语句,返回Unit。所以写代码块时,确保最后一行是你想要返回的值。

好了,这一章的内容就到这里。变量与常量、基本数据类型、字符串插值、代码块与表达式,这些都是Scala的基石。别小看它们,我在实际项目中,80%的bug都跟这些基础概念有关。下一章我们聊聊函数,那才是Scala真正发力的地方。