Scala 游览

基础知识

语言

在本页面中,我们将介绍 Scala 的基础知识。

在浏览器中尝试 Scala

您可以使用 Scastie 在浏览器中运行 Scala。这是一种简单、无需设置的方式来试验 Scala 代码片段

  1. 转到 Scastie
  2. 在左窗格中粘贴 println("Hello, world!")
  3. 单击运行。输出将显示在右窗格中。

Scastie 与本文档中的一些代码示例集成在一起;如果您在下面的代码示例中看到运行按钮,请单击该按钮直接试验代码。

表达式

表达式是可以计算的语句

1 + 1

您可以使用 println 输出表达式的结果

println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!

您可以使用 val 关键字来命名表达式的结果

val x = 1 + 1
println(x) // 2

命名的结果(如这里的 x)称为值。引用值不会重新计算它。

值无法重新分配

x = 3 // This does not compile.

值的类型可以省略并 推断,也可以明确声明

val x: Int = 1 + 1

请注意类型声明 Int 如何位于标识符 x 之后。您还需要一个 :

变量

变量类似于值,但您可以重新分配它们。您可以使用 var 关键字定义变量。

var x = 1 + 1
x = 3 // This compiles because "x" is declared with the "var" keyword.
println(x * x) // 9

与值一样,变量的类型可以省略并推断,也可以明确声明

var x: Int = 1 + 1

你可以通过用 {} 包围表达式来组合它们。我们称之为块。

块中最后一个表达式的结果也是整个块的结果

println({
  val x = 1 + 1
  x + 1
}) // 3

函数

函数是有参数并接受参数的表达式。

你可以定义一个匿名函数(即没有名称的函数),它返回给定的整数加一

(x: Int) => x + 1

=> 的左侧是参数列表。右侧是涉及参数的表达式。

你也可以命名函数

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

方法

方法的外观和行为与函数非常相似,但它们之间有一些关键的区别。

方法使用 def 关键字定义。 def 后面跟着一个名称、参数列表、一个返回类型和一个主体

def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3

请注意,返回类型 Int 是在参数列表和 : 之后声明的。

一个方法可以采用多个参数列表

def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
println(addThenMultiply(1, 2)(3)) // 9

或者根本没有参数列表

def name: String = System.getProperty("user.name")
println("Hello, " + name + "!")

还有一些其他区别,但现在,你可以将方法视为类似于函数的东西。

方法也可以有多行表达式

def getSquareString(input: Double): String = {
  val square = input * input
  square.toString
}
println(getSquareString(2.5)) // 6.25
def getSquareString(input: Double): String =
  val square = input * input
  square.toString

println(getSquareString(2.5)) // 6.25

主体中的最后一个表达式是方法的返回值。(Scala 确实有一个 return 关键字,但它很少使用。)

你可以使用 class 关键字定义类,后面跟着它的名称和构造函数参数

class Greeter(prefix: String, suffix: String) {
  def greet(name: String): Unit =
    println(prefix + name + suffix)
}
class Greeter(prefix: String, suffix: String):
  def greet(name: String): Unit =
    println(prefix + name + suffix)

方法 greet 的返回类型是 Unit,表示没有有意义的东西可以返回。它与 Java 和 C 中的 void 类似。(不同之处在于,由于每个 Scala 表达式都必须具有一些值,因此实际上有一个类型为 Unit 的单例值,写为 ()。它不携带任何信息。)

在 Scala 2 中,你可以使用 new 关键字实例化一个类。然而,在 Scala 3 中,由于通用应用方法,不需要 new 关键字

val greeter = new Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!
val greeter = Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!

我们将在稍后深入介绍类。

案例类

Scala 有一种特殊类型的类,称为“案例”类。默认情况下,案例类的实例是不可变的,并且它们通过值进行比较(与通过引用比较其实例的类不同)。这使得它们对于 模式匹配 更加有用。

你可以使用 case class 关键字定义案例类

case class Point(x: Int, y: Int)

你可以在没有 new 关键字的情况下实例化案例类

val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)

案例类的实例通过值进行比较,而不是通过引用

if (point == anotherPoint) {
  println(s"$point and $anotherPoint are the same.")
} else {
  println(s"$point and $anotherPoint are different.")
} // Point(1,2) and Point(1,2) are the same.

if (point == yetAnotherPoint) {
  println(s"$point and $yetAnotherPoint are the same.")
} else {
  println(s"$point and $yetAnotherPoint are different.")
} // Point(1,2) and Point(2,2) are different.
if point == anotherPoint then
  println(s"$point and $anotherPoint are the same.")
else
  println(s"$point and $anotherPoint are different.")
// ==> Point(1,2) and Point(1,2) are the same.

if point == yetAnotherPoint then
  println(s"$point and $yetAnotherPoint are the same.")
else
  println(s"$point and $yetAnotherPoint are different.")
// ==> Point(1,2) and Point(2,2) are different.

还有更多关于案例类的内容我们想要介绍,我们相信你会爱上它们!我们将在 稍后 深入介绍它们。

对象

对象是其自身定义的单个实例。你可以将它们视为其自身类的单例。

你可以使用 object 关键字定义对象

object IdFactory {
  private var counter = 0
  def create(): Int = {
    counter += 1
    counter
  }
}
object IdFactory:
  private var counter = 0
  def create(): Int =
    counter += 1
    counter

你可以通过引用其名称来访问对象

val newId: Int = IdFactory.create()
println(newId) // 1
val newerId: Int = IdFactory.create()
println(newerId) // 2

我们将在 稍后 深入介绍对象。

特质

特质是包含某些字段和方法的抽象数据类型。在 Scala 继承中,一个类只能扩展另一个类,但它可以扩展多个特质。

你可以使用 trait 关键字定义特质

trait Greeter {
  def greet(name: String): Unit
}
trait Greeter:
  def greet(name: String): Unit

特质也可以有默认实现

trait Greeter {
  def greet(name: String): Unit =
    println("Hello, " + name + "!")
}
trait Greeter:
  def greet(name: String): Unit =
    println("Hello, " + name + "!")

你可以使用 extends 关键字扩展特质,并使用 override 关键字覆盖实现

class DefaultGreeter extends Greeter

class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {
  override def greet(name: String): Unit = {
    println(prefix + name + postfix)
  }
}

val greeter = new DefaultGreeter()
greeter.greet("Scala developer") // Hello, Scala developer!

val customGreeter = new CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala developer") // How are you, Scala developer?
class DefaultGreeter extends Greeter

class CustomizableGreeter(prefix: String, postfix: String) extends Greeter:
  override def greet(name: String): Unit =
    println(prefix + name + postfix)

val greeter = DefaultGreeter()
greeter.greet("Scala developer") // Hello, Scala developer!

val customGreeter = CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala developer") // How are you, Scala developer?

在此,DefaultGreeter 仅扩展一个特质,但它可以扩展多个特质。

我们将在 稍后 深入介绍特质。

程序入口点

main 方法是 Scala 程序的入口点。Java 虚拟机需要一个名为 main 的 main 方法,该方法接受一个参数:一个字符串数组。

在 Scala 2 中,你必须手动定义一个 main 方法。使用对象,你可以按如下方式定义 main 方法

object Main {
  def main(args: Array[String]): Unit =
    println("Hello, Scala developer!")
}

在 Scala 3 中,使用 @main 注解,可以按如下方式从方法自动生成 main 方法

@main def hello() = println("Hello, Scala developer!")

更多资源

此页面的贡献者