3、Scala基础语法:变量与常量、基本数据类型、字符串插值、代码块与表达式
好,咱们今天聊聊Scala最基础的东西。说实话,很多初学者觉得语法嘛,背一背就行了。但我告诉你,这里面的门道可不少。我在大数据项目中见过太多因为变量声明不当引发的血案了。
3.1 变量与常量:val vs var
Scala里声明变量有两种方式:val 和 var。这俩的区别,说白了就是一个不能改,一个能改。
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真正发力的地方。