- Tour of Scala,中文版
- Scala Online Playground
- A Compact Benchmark in C++, Java, Go, and Scala from Google(PDF)
Basic
// Types
// Any is the supertype of all types, Nothing is the subtype of all types
// String
val x = -1
println(s"x.abs = ${x.abs}") // "x.abs = 1"
val name = "James"
val age = 30
println(s"$name is $age years old") // "James is 30 years old"
// Functions
val addOne = (x: Int) => x + 1
println(addOne(1)) // 2
// Methods
// note: The last expression in the body is the method’s return value.
def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3
// Classes
// note: void in C++, Unit in Scala
// note: In Scala 3 you can make an instance of a class without new keyword
// default parameters
class Point(var x: Int = 0, var y: Int = 0)
//
class Point:
private var _x = 0
def x: Int = _x
def x_=(newValue: Int): Unit = // def x_= is for validating and setting the value of _x
_x = newValue
end Point
val point1 = Point()
point1.x = 99
// Traits
// cannot have constructor parameters
trait Iterator[A]:
def hasNext: Boolean
def next(): A
class IntIterator(to: Int) extends Iterator[Int]:
private var current = 0
override def hasNext: ???
override def next(): ???
end IntIterator
// where a given trait is required, a subtype of the trait can be used instead:
trait Pet:
val name: String
class Cat(val name: String) extends Pet
val cat = Cat("Sally")
// Tuple
// a tuple can hold a sequence of different types.
// Scala3:
println(ingredient(0)) // Sugar
println(ingredient(1)) // 25
// Scala2:
println(ingredient._1) // Sugar
println(ingredient._2) // 25
//
val (name, quantity) = ingredient
println(name) // Sugar
println(quantity) // 25
//
val numPairs = List((2, 5), (3, -7), (20, 56))
for (a, b) <- numPairs do
println(a * b)
// Seq
// an ordered collection of elements (also called a sequence)
val numbers = Seq (1, 15, -2, 0)
val second = numbers (1)
// Array // fill n element Array.fill(n)(element) // fill n block return values Array.fill(n){ block }
类混合
TODO
函数是一等公民
functions are first-class values in Scala
Use the phrase “higher order function” for both methods and functions that take functions as parameters or that return a function:
val salaries = Seq(20_000, 70_000, 40_000)
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
// or just
val newSalaries = salaries.map(_ * 2)
Nest method definitions:
def factorial(x: Int): Int =
def fact(x: Int, accumulator: Int): Int =
if x <= 1 then accumulator
else fact(x - 1, x * accumulator)
fact(x, 1)
模式匹配
sealed
关键字可以让编译器检查case是否考虑完全.
// Matching on case classes
sealed trait Notification // This provides extra safety because the compiler checks that the cases of a match expression are exhaustive when the base type is sealed
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String =
notification match
case Email(sender, _, _) if importantPeopleInfo.contains(sender) =>
"You got an email from special someone!"
case SMS(number, _) if importantPeopleInfo.contains(number) =>
"You got an SMS from special someone!"
case other =>
showNotification(other) // nothing special, delegate to our original showNotification function
val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com")
val someSms = SMS("123-4567", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!")
val importantSms = SMS("867-5309", "I'm here! Where are you?")
println(showImportantNotification(someSms, importantPeopleInfo)) // prints You got an SMS from 123-4567! Message: Are you there?
println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) // prints You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
println(showImportantNotification(importantEmail, importantPeopleInfo)) // prints You got an email from special someone!
println(showImportantNotification(importantSms, importantPeopleInfo)) // prints You got an SMS from special someone!
伴随对象
Like static in C++ Class?
import scala.math.{Pi, pow}
case class Circle(radius: Double):
import Circle.*
def area: Double = calculateArea(radius)
object Circle:
private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
val circle1 = Circle(5.0)
circle1.area
Factory method:
class Email(val username: String, val domainName: String)
object Email:
def fromString(emailString: String): Option[Email] =
emailString.split('@') match
case Array(a, b) => Some(Email(a, b))
case _ => None
val scalaCenterEmail = Email.fromString("scala.center@epfl.ch")
scalaCenterEmail match
case Some(email) => println(
s"""Registered an email
|Username: ${email.username}
|Domain name: ${email.domainName}
""".stripMargin)
case None => println("Error: could not parse email")
apply和unapply
import scala.util.Random
object CustomerID {
def apply(name: String) = s"$name--${Random.nextLong()}"
def unapply(customerID: String): Option[String] = {
val stringArray: Array[String] = customerID.split("--")
if (stringArray.tail.nonEmpty) Some(stringArray.head) else None
}
}
val customer1ID = CustomerID("Sukyoung") // Sukyoung--23098234908
customer1ID match {
case CustomerID(name) => println(name) // prints Sukyoung
case _ => println("Could not extract a CustomerID")
}
val customer2ID = CustomerID("Nico")
val CustomerID(name) = customer2ID
println(name) // prints Nico
当val customer1ID = CustomerID("Sukyoung")
时,调用apply()
.当CustomerID(name) => println(name)
时,调用unapply()
型变 Variance
泛型父子关系控制?
class Foo[+A] // A covariant class
class Bar[-A] // A contravariant class
class Baz[A] // An invariant class
协变[+T]
given some class Cov[+T], then if A is a subtype of B, Cov[A] is a subtype of Cov[B]
逆变[-T]
given some class Contra[-T], then if A is a subtype of B, Contra[B] is a subtype of Contra[A]
例如一个序列化器:
abstract class Animal {
def name: String
}
case class Cat(name: String) extends Animal
abstract class Serializer[-A]:
def serialize(a: A): String
val animalSerializer: Serializer[Animal] = new Serializer[Animal]():
def serialize(animal: Animal): String = s"""{ "name": "${animal.name}" }"""
val catSerializer: Serializer[Cat] = animalSerializer
catSerializer.serialize(Cat("Felix"))
不变[T]
given some class Inv[T], then although A is a subtype of B, Inv[A] is not a subtype of Inv[B]
类型边界约束
类型上界
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
T <: A
这样声明的类型上界表示类型变量T
应该是类型A
的子类
类型下界
>: A
表示类型参数B
或抽象类型B
是类型A
的超类型
其他糖
- 在 Scala 中,
???
是一个便捷的方法,用于表示一个尚未实现的代码部分。 - for Comprehensions
def foo(n: Int, v: Int) =
for i <- 0 until n
j <- 0 until n if i + j == v
yield (i, j)
foo(10, 10).foreach {
(i, j) => println(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) (6, 4) (7, 3) (8, 2) (9, 1)
}
和C/C++的不同
- Scala中的类构造参数自动成为成员变量,而C++构造函数中参数只是局部变量,在Scala中想要获得类似的效果,应该让类构造参数变成私有的
- Scala中函数是一等公民