序言:Scala的特性概览--旋风之旅

  在这本书中,我们希望证明Scala是优美的,现代的,富于表现力的编程语言。为了证明这一点,在这第一章节中,我们将直接介绍Scala的主要特性。在这之后,这本书会以一个更传统的“入门”章节开始。

	本书假设你曾用过一种像java那样的语言,准备来看一系列Scala的例子,来感受一下这门语言吧。

概述

  在进入具体示例之前,这里有一些关于Scala的重要事情需要知道:

  • 它是一门高级语言
  • 它是静态类型的
  • 它的语法是简洁的但仍然易于阅读 — 我们称之为富有表现力
  • 它支持面向对象编程范式
  • 它支持函数式编程范式
  • 它有一个复杂的类型推理系统
  • Scala代码编译成在Java虚拟机上运行的 .class 文件
  • 在Scala中很容易使用Java类库

Hello World

自从《C编程语言》一书问世以来,编程书中以“Hello World”作为示例开始已经成为传统,不要失望,这里有用Scala完成这个示例的一种方式:

object Hello extends App {
	println("Hello,world.")
}

你把这段代码保存在命名为 Hello.scala 的一个文件中之后,你可以使用 scalac 编译它。

$ scalac Hello.scala

如果你是从Java 来到Scala的,scalac 就像 javac , 这个命令产生两个文件:

  • Hello$.class
  • Hello.class
    这些与你用 javac 创建的 .class 字节码文件是一样的,它们已经准备好运行在JVM上了。你可以使用 scala 运行 Helllo 程序:
$ scala Hello

在接下来的课程中,我们会分享更多“Hello World”的例子,现在先不作介绍。

The Scala REPL

The Scala REPL (“Read-Evaluate-Print-Loop”) 是一个命令行解释器,你可以将它作为一个“游乐场”来测试你的Scala代码。我们在这里很早的介绍它,因此你可以在下面的代码示例中使用它:

在你的操作系统的命令行输入 scala 就可以进入一个 REPL 会话,你会看到这样的东西:

$ scala
Welcome to Scala 2.13.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.

scala> 

REPL是一个解释器,它就在那里等待你输入一些东西。在REPL中,你输入一些Scala表达式来看看它是如何运行的:

scala> val x = 1
x: Int = 1

scala> val y = x + 1
y: Int = 2

就像上面显示的,每次你在REPL输入一行表达式后,会在下一行输出结果。

变量的两种类型

Scala有两种类型的变量:

  • val 表示一个不可变的变量,类似Java中的 final, 应该被优先选择使用。
  • var 表示一个可变的变量,只有在有特定原因需要使用它时才使用它。

示例:

val x = 1   //immutable
var y = 0   //mutable

声明变量类型

在Scala中,你在创建变量时通常不会声明它们的类型:

val x = 1
val s = "a string"
val p = new Person("Regina")

当你这样做时,Scala通常可以为你推断出数据类型。就像在这个REPL中显示的:

scala> val x = 1
val x: Int = 1

scala> val s = "a string"
val s: String = a string

这个特性被称为 类型推断,它是一个帮助你保持代码简洁的好方法。你也可以明确的声明一个变量的类型,但通常不是必须的:

val x: Int = 1
val s: String = "a string"
val p: Person = new Person("Regina")

正如你看到的,上面代码有些不必要的冗长。

控制结构

下面是Scala控制结构的快速浏览。

if/else

Scala的 if/else 控制结构与其他语言是相似的。

if (test1) {
    doA()
} else if (test2) {
    doB()
} else if (test3) {
    doC()
} else {
    doD()
}

然而,与Java和很多其他语言不同的是,这里的 if/else结构返回一个值,因此,你可以把它用作三元运算符:

val x = if (a < b) a else b
match 表达式

Scala有一个 match 表达式,它的最基本用法像 Java 的 switch 语句:

val result = i match {
    case 1 => "one"
    case 2 => "two"
    case _ => "not 1 or 2"
}

match 表达式不只限于整数,它可用于任何数据类型,包括布尔型:

val booleanAsString = bool match {
    case true => "true"
    case false => "false"
}

下面有个match被用作方法体的示例,来匹配很多不同的类型:

def getClassAsString(x: Any):String = x match {
    case s: String => s + " is a String"
    case i: Int => "Int"
    case f: Float => "Float"
    case l: List[_] => "List"
    case p: Person => "Person"
    case _ => "Unknown"
}

强大的匹配表达式是Scala的一大特性,在本书的后面我们会展示更多它的示例。

try/catch

Scala的 try/catch 控制结构可以捕获异常,它与Java类似,但它的语法是与 match 表达式一致的:

try {
    writeToFile(text)
} catch {
    case fnfe: FileNotFoundException => println(fnfe)
    case ioe: IOException => println(ioe)
}
for 循环及其表达式

Scala 的 for 循环,在本书中,通常写成如下的样子:

for (arg <- args) println(arg)

// "x to y" syntax
for (i <- 0 to 5) println(i)

// "x to y by" syntax
for (i <- 0 to 10 by 2) println(i)

你也可以在for循环中添加 yield 关键字变成 for 表达式,这个表达式会产生一个结果。
下面的一个 for 表达式,按照 1 到 5 的顺序,把每个值加倍:

val x = for (i <- 1 to 5) yield i * 2

下面的一个 for 表达式 遍历一个字符串列表:

val fruits = List("apple", "banana", "lime", "orange")

val fruitLengths = for {
    f <- fruits
    if f.length > 4
} yield f.length

因为Scala代码通常是很直观地,我们可以想象你能够猜出这段代码是如何运行的,即使你没有看多 for 表达式或Scala List 。

while 和 do/while

Scala 也有 whiledo/while ,下面是它的一般语法:

// while loop
while(condition) {
    statement(a)
    statement(b)
}

// do-while
do {
   statement(a)
   statement(b)
} 
while(condition)

下面是一个Scala 类的例子:

class Person(var firstName: String, var lastName: String) {
    def printFullName() = println(s"$firstName $lastName")
}

下面是你如何用这个类:

val p = new Person("Julia", "Kern")
println(p.firstName)
p.lastName = "Manes"
p.printFullName()

请注意,这里不需要创建 setget 方法来访问类中的字段。

下面有个更复杂的类,这个 Pizza 类在本书的后面还会见到:

class Pizza (
    var crustSize: CrustSize,
    var crustType: CrustType,
    val toppings: ArrayBuffer[Topping]
) {
    def addTopping(t: Topping): Unit = toppings += t
    def removeTopping(t: Topping): Unit = toppings -= t
    def removeAllToppings(): Unit = toppings.clear()
}

在上面的代码中,ArrayBuffer 就像 Java 中的 ArrayListCrustSize, CrustType, 和Topping 类没有展示,但没有看到这些类,你也很可能是理解代码是如何运行的。

Scala 方法

就像别的 OOP(面向对象) 语言,Scala 类有方法,下面是Scala 方法的语法:

def sum(a: Int, b: Int): Int = a + b
def concatenate(s1: String, s2: String): String = s1 + s2

你不必必须声明一个方法的返回类型,所以按照下面的写法也是完全合法的,如果你更喜欢如此:

def sum(a: Int, b: Int) = a + b
def concatenate(s1: String, s2: String) = s1 + s2

下面是你如何调用方法:

val x = sum(1, 2)
val y = concatenate("foo", "bar")

你可以用方法做更多的事情,例如给方法参数提供默认的值,但是,现在上面的就是一个好的开始哦。

特质

在Scala中,特质是很有趣的。它们可以让你把代码分解成更小的模块单元。为了展示特质,下面有个从书中后面拿过来的例子:
给定了三个特质:

trait Speaker {
    def speak(): String  // has no body, so it’s abstract
}

trait TailWagger {
    def startTail(): Unit = println("tail is wagging")
    def stopTail(): Unit = println("tail is stopped")
}

trait Runner {
    def startRunning(): Unit = println("I’m running")
    def stopRunning(): Unit = println("Stopped running")
}

你可以创建一个 Dog 类,扩展自上面的这些特质同时为 speak( ) 方法提供方法体。

class Dog(name: String) extends Speaker with TailWagger with Runner {
    def speak(): String = "Woof!"
}

类似的,下面是一个 Cat 类,展示了如何去重写多个特质的方法:

class Cat extends Speaker with TailWagger with Runner {
    def speak(): String = "Meow"
    override def startRunning(): Unit = println("Yeah ... I don’t run")
    override def stopRunning(): Unit = println("No need to stop")
}

如果上面的代码你觉得有意义,太好了,你对特质很满意,如果觉得没有意义,别担心,在后面我们会详细的解释它。

集合类

如果你是从Java转到Scala,并且准备真正的深入学习Scala, 很可能在Scala中使用Java类库,并且一些人这样做了几周或几个月同时对Scala也感到很满意。但强烈推荐你尽快地学习基本的Scala集合类 ---- List, ListBuffer, Vector, ArrayBuffer, Map, 和 Set。Scala集合类的一个很大的好处是它们提供了很多强大的方法,你会很快地开始使用它们来精简你的代码。

填充列表

有时创建填充了数据的样本列表是很有用的,Scala提供了很多方法来填充列表,下面是其中的一些:

val nums = List.range(0, 10)
val nums = (1 to 10 by 2).toList
val letters = ('a' to 'f').toList
val letters = ('a' to 'f' by 2).toList
序列方法

虽然有很多顺序集合类可以使用 ---- Array, ArrayBuffer, Vector, List等等,让我们来看一些用List类可以做什么的例子,给定下面两个列表:

val nums = (1 to 10).toList
val names = List("joel", "ed", "chris", "maurice")

下面是foreach方法:

scala> names.foreach(println)
joel
ed
chris
maurice

下面是filter方法,后接foreach方法:

scala> nums.filter(_ < 4).foreach(println)
1
2
3

下面是map方法的一些例子:

scala> val doubles = nums.map(_ * 2)
doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

scala> val capNames = names.map(_.capitalize)
capNames: List[String] = List(Joel, Ed, Chris, Maurice)

scala> val lessThanFive = nums.map(_ < 5)
lessThanFive: List[Boolean] = List(true, true, true, true, false, false, false, false, false, false)

即使没有任何解释你也可以明白map是如何运行的:它将你提供的算法应用于集合中的每个元素,为每个元素返回一个新的转换后的值。

如果你准备好了,来看其中一个最强大的集合方法,就是 foldLeft

scala> nums.foldLeft(0)(_ + _)
res0: Int = 55

scala> nums.foldLeft(1)(_ * _)
res1: Int = 3628800

如果你知道foldLeft的第一个参数是种子值,你可以猜到,第一个例子输出的是 nums 中数的和,第二个例子输出的是nums 中数的积。
Scala集合类还有许多(许多哦!)可以使用的方法,会在本书后面的集合部分展示。很希望上面的让你了解到它的强大。

元组

元组让你可以把一些不同类型的元素集合放入到一个小容器中。元组可以包含 0 到 22 个值,即使类型全不一样。
例如,给定一个 Person 类:

class Person(var name: String)

你可以创建一个元组包含三种不同的类型:

val t = (11, "Eleven", new Person("Eleven"))

你可以通过序号访问元组中的元素:

t._1
t._2
t._3

还可以将元组字段分配给变量:

val (num, string, person) = (11, "Eleven", new Person("Eleven"))

元组对于那些需要把一些小“包”先暂时放在一起的时候是很有用的。

还没有展示的

虽然上面已经介绍了不少,但是还有很多内容没有介绍:

  • 字符串和内置数值类型
  • 包和导入
  • 如何在Scala中使用Java集合类
  • 如何在Scala中使用Java库
  • 如何构建Scala项目
  • 如何在Scala中执行单元测试
  • 如何编写Scala shell 脚本
  • Map,Set 和其他集合类
  • 面向对象编程
  • 函数式编程
  • 使用Future的并发
  • 等等。。。。。。
    如果你觉得看过的上面的不错,希望后面的也得到你的喜欢。

一点背景

Scala是由 Martin Odersky 创造,他曾在 Niklaus Wirth 手下学习,后者创建了Pascal和其他几种语言, Mr. Odersky 是 Generic Java的共同设计者之一,也被称为javac编译器之父。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值