专栏原创出处:github-源笔记文件 (opens new window) ,github-源码 (opens new window),欢迎 Star,转载请附上原文出处链接和本声明。
Scala 编程语言专栏系列笔记,系统性学习可访问个人复盘笔记-技术博客 Scala 编程语言 (opens new window)
# 什么是表达式
在 Scala 中,一切皆可以视为表达式,一个语句块即为一个表达式,表达式可以是一行语句或多行语句。
- 多行表达式,用
{}
包含即可。 - 表达式具有返回值,多行表达式最后一行的结果即为表达式的返回值。
- 在 Scala 中,
val
定义不可变的变量,相当于 Java 中加了final
关键字限定,var
定义可以重新赋值的变量。
// 输出内容
println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!
// 定义变量
val x = 1 + 1
println(x) // 2
x = 3 // This does not compile.
var y: Int = 1 + 1
y=4
// 定义函数表达式
val p = (x: Int) => x + 1
// 多行表达式
val result = {
val a = 100
val b = 200
a + b
}
println("result = " + result) // result = 300
# 什么是函数
Scala 中,通常函数指的是一段代码逻辑的集合,可以有输入和输出,用来封装一段逻辑或者功能。
使用 val 关键字来定义函数,形式: val 函数名 = (参数) => 功能
。
- 函数在定义的时候就会被加载计算。
- 函数必须带有参数列表,有参或者无参。
val addOne = (x: Int) => x + 1
println(addOne(1)) // 2
val add = (x: Int, y: Int) => x + y
println(add(1, 2)) // 3
val getTheAnswer = () => 42
println(getTheAnswer()) // 42
// 因为函数在定义的时候就会被加载计算,因此 test1 方法的结果是固定的。
val test1: () => Int = {
val r = util.Random.nextInt
() => r
}
println(test1()) // 78
println(test1()) // 78
# 什么是方法
方法(Method)是函数的特殊形式,指的是类或者对象中定义的函数成员。
使用 def 关键字来定义方法,def 后面跟着一个名字、参数列表、返回类型和方法体。
- 方法在被调用的时候,然后再进行计算。
- 方法可以没有参数列表。
- 方法可以强制转换为函数,在方法后加 _ 即可。
// 有参方法
def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3
// 无参方法
def name: String = System.getProperty("user.name")
println("Hello, " + name + "!")
// 多参数列表方法
def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
println(addThenMultiply(1, 2)(3)) // 9
// 采用多行表达式定义方法
def getSquareString(input: Double): String = {
val square = input * input
square.toString
}
// 可以与函数举例中的 test1() 进行对比,方法在使用时才会计算,因此每次调用的结果都不同。
def test2: () => Int = {
val r = util.Random.nextInt
() => r
}
println(test2()) // 100
println(test2()) // 200
# 类型推断
当定义变量或者方法、函数时,不指定变量的类型或者函数的返回值类型,编译器可以自动推断出类型。
val businessName = "Montreux Jazz Café" // String
def squareOf(x: Int) = x * x // 编译器可以推断出方法的返回类型为 Int
case class MyPair[A, B](x: A, y: B)
val p = MyPair(1, "scala") // type: MyPair[Int, String]
def id[T](x: T) = x
val q = id(1) // type: Int
//何时 不要 依赖类型推断
var obj = null // 等价于 var obj: Null = null
obj = new AnyRef // 它不能编译,因为 obj 推断出的类型是 Null。
// 由于该类型的唯一值是 null,因此无法分配其他的值。
# 默认参数值
Scala 具备给参数提供默认值的能力,这样调用者就可以忽略这些具有默认值的参数。
// log 方法的参数 level 具有默认值,可以为其重新赋值也可以使用默认值。
def log(message: String, level: String = "INFO"): Unit = println(s"$level: $message")
log("System starting") // prints INFO: System starting
log("User not found", "WARNING") // prints WARNING: User not found
// point1 只有 y 指定了新的值,剩余 x 和 z 都使用了默认值 0
class Point(val x: Double = 0, val y: Double = 0, val z: Double = 0)
val point1 = new Point(y = 1)
// 注意从 Java 代码中调用时,Scala 中的默认参数则是必填的(非可选),如:
/*
public class Main {
public static void main(String[] args) {
Point point = new Point(1); // does not compile
}
}
*/
# 什么是命名参数
当调用方法时,实际参数可以用其对应的形式参数的名称来接收,进行赋值。
注意调用 Java 方法时不能使用命名参数。
{
def printName(first: String, last: String): Unit = {
println(first + " " + last)
}
printName("John", "Smith") // Prints "John Smith"
printName(first = "John", last = "Smith") // Prints "John Smith"
printName(last = "Smith", first = "John") // Prints "John Smith"
// 注意使用命名参数时,顺序是可以重新排列的。
// 如果只有部分参数被命名了,则所有参数应按照方法定义的参数顺序进行排列。
printName(last = "Smith", "john") // error: positional after named argument
}
# 什么是传名参数
定义一个传名参数,只需在它的类型前加上 =>
- 传名参数仅在被使用时才会触发实际参数的求值运算。它们与传值参数正好相反。
- 传值参数的优点是仅会被计算一次,如果方法中重复使用了参数代码块,传名参数会被重复计算。
- 如果参数是计算密集型或长时间运行的代码块,这种延迟计算参数、直到它被使用时才计算的能力可以帮助提高性能。
// 定义一个传名参数 input
def calculate(input: => Int): Int = input * 37
// 方法 whileLoop 使用多个参数列表来分别获取循环条件和循环体。
// 如果 condition 为 true,则执行 body,然后对 whileLoop 进行递归调用。
// 如果 condition 为 false,则永远不会计算 body,因为我们在 body 的类型前加上了 =>。
@scala.annotation.tailrec
def whileLoop(condition: => Boolean)(body: => Unit): Unit =
if (condition) {
body
whileLoop(condition)(body)
}
var i = 2
whileLoop(i > 0) {
println(i)
i -= 1
} // prints 2 1
# 运算符
当一个表达式使用多个运算符时,将根据运算符的第一个字符来评估优先级。
/*
* (characters not shown below)
* / %
* + -
* :
* = !
* < >
* &
* ^
* |
* (all letters)
**/
// a + b ^? c ?^ d less a ==> b | c 等价于 ((a + b) ^? (c ?^ d)) less ((a ==> b) | c)
统一类型 →