专栏原创出处: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)
最后修改时间: 2/17/2020, 4:43:04 AM