scala包和java包的声明一样在源文件的第一行;
scala包的导入和java包有些不同,java导入包使用import关键字
scala导入包也是import但是有几种不同的写法:
import java.util.*
;这种写法,但是scala不能这种写法而是 import java.util._
;import java.uitl.{List,ArrayList} import java.awt.{Color, Font} // 重命名成员 import java.util.{HashMap => JavaHashMap} // 隐藏成员 import java.util.{HashMap => _, _} // 引入了util包的所有成员,但是HashMap被隐藏了
Scala 使用 package
关键字定义包,在Scala将代码定义到某个包中有两种方式:
package com.runoob class HelloWorld{}
package com.runoob { class HelloWorld {} }
第二种方法,可以在一个文件中定义多个包。
注意:默认情况下,Scala 总会引入 java.lang._ 、 scala._ 和 Predef._,这里也能解释,为什么以scala开头的包,在使用时都是省去scala.的。
abstract | case | catch | class |
---|---|---|---|
def | do | else | extends |
false | final | finally | for |
forSome | if | implicit | import |
lazy | match | new | null |
object | override | package | private |
protected | return | sealed | super |
this | throw | trait | try |
true | type | val | var |
while | with | yield |
数据类型
Scala与Java具有相同的数据类型,具有相同的内存占用和精度。以下是提供Scala中可用的所有数据类型的详细信息的表格:
序号 | 数据类型 | 说明 |
---|---|---|
1 | Byte | 8位有符号值,范围从-128至127 |
2 | Short | 16位有符号值,范围从-32768至32767 |
3 | Int | 32位有符号值,范围从-2147483648至2147483647 |
4 | Long | 64位有符号值,范围从-9223372036854775808至9223372036854775807 |
5 | Float | 32位IEEE 754单精度浮点值 |
6 | Double | 64位IEEE 754双精度浮点值 |
7 | Char | 16位无符号Unicode字符。范围从U+0000到U+FFFF |
8 | String | 一个Char类型序列 |
9 | Boolean | 文字值true或文字值false |
10 | Unit | 对应于无值 |
11 | Null | null或空引用 |
12 | Nothing | 每种其他类型的亚型; 不包括无值 |
13 | Any | 任何类型的超类型; 任何对象的类型为Any |
14 | AnyRef | 任何引用类型的超类型 |
上面列出的所有数据类型都是对象。
Scala中没有类似Java中那样的原始类型。这意味着您可以调用Int,Long等方法。
val 定义变量相当于java 的final不可变
val x = 1 val s = "a string" val p = new Person("Regina")
或
val x: Int = 1 val s: String = "a string" val p: Person = new Person("Regina")
Scala带有您期望的标准数字数据类型。在Scala中,所有这些数据类型都是成熟的对象(不是原始数据类型)。
这些示例说明如何声明基本数字类型的变量:
val b: Byte = 1 val x: Int = 1 val l: Long = 1 val s: Short = 1 val d: Double = 2.0 val f: Float = 3.0
在第四个例子,如果你没有明确指定类型,数量1将默认为Int,所以如果你想在其他数据类型中的一种- Byte,Long或者Short-你需要显式声明的类型,如图所示。带小数的数字(如2.0)将默认为a Double,因此,如果需要a Float,则需要声明a Float,如最后一个示例所示。
For large numbers Scala also includes the types BigInt and BigDecimal:
var b = BigInt(1234567890) var b = BigDecimal(123456.789)
Scala also has String
and Char
data types, which you can generally declare with the implicit form:
val name = "Bill" val c = 'a'
Scala字符串有很多不错的特性,但是我们想花点时间来强调两个特性,我们将在本书的其余部分中使用它们。第一个特性是Scala有一个很好的、类似Ruby的方法来合并多个字符串。考虑到这三个变量:
val firstName = "John" val mi = 'C' val lastName = "Doe"
you can append them together like this, if you want to:
val name = firstName + " " + mi + " " + lastName
However, Scala provides this more convenient form:
val name = s"$firstName $mi $lastName"
如图所示,您只需在字符串前面加上字母s,然后在字符串中变量名前面加上$符号。此功能称为string interpolation
。
与Java和许多其他语言不同,if / else构造返回一个值,因此,除其他外,您可以将其用作三元运算符:
val x = if (a < b) a else b
Scala有一个match表达式,其最基本的用法就像一个Java switch语句:
val result = i match { case 1 => "one" case 2 => "two" case _ => "not 1 or 2" }
这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控制结构使您可以捕获异常。它类似于Java,但是其语法与match表达式一致:
try { writeToFile(text) } catch { case fnfe: FileNotFoundException => println(fnfe) case ioe: IOException => println(ioe) }
try { // your scala code here } catch { case foo: FooException => handleFooException(foo) case bar: BarException => handleBarException(bar) case _: Throwable => println("Got some other kind of Throwable exception") } finally { // your scala code here, such as closing a database connection // or file handle }
Scala for循环-我们通常在本书中将其称为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)
您还可以将yield关键字添加到for循环中,以创建产生结果的for表达式。这是一个for表达式,它将序列1到5中的每个值加倍:
val x = for (i <- 1 to 5) yield i * 2 //返回一个Vector
这是另一个针对字符串列表的for表达式:
val fruits = List("apple", "banana", "lime", "orange") val fruitLengths = for { f <- fruits if f.length > 4 } yield f.length
Using for and foreach with Maps
You can also use for
and foreach
when working with a Scala Map
(which is similar to a Java HashMap
). For example, given this Map
of movie names and ratings:
val ratings = Map( "Lady in the Water" -> 3.0, "Snakes on a Plane" -> 4.0, "You, Me and Dupree" -> 3.5 ) //for 循环 for ((name,rating) <- ratings) println(s"Movie: $name, Rating: $rating") //或 for 表达式 ratings.foreach { case(movie, rating) => println(s"key: $movie, value: $rating") }
// while loop while(condition) { statement(a) statement(b) } // do-while do { statement(a) statement(b) } while(condition)
就像其他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
class Dog(name: String) extends Speaker with TailWagger with Runner { def speak(): String = "Woof!" }
类 | 描述 |
---|---|
ArrayBuffer | 索引的可变序列 |
List | 线性(链表),不可变序列 |
Vector | 索引不变的序列 |
Map | 基Map(键/值对)类 |
Set | 基Set类 |
Map和Set有可变版本和不可变版本。
这是一个可变序列,因此您可以使用其方法来修改其内容,并且这些方法类似于Java序列上的方法。
要使用,ArrayBuffer您必须先将其导入:
import scala.collection.mutable.ArrayBuffer val ints = ArrayBuffer[Int]() val names = ArrayBuffer[String]()
ArrayBuffer就可以通过多种方式向其中添加元素
val ints = ArrayBuffer[Int]() ints += 1 ints += 2
这只是创建ArrayBuffer和添加元素的一种方法。您还可以ArrayBuffer使用以下初始元素创建一个:
val nums = ArrayBuffer(1, 2, 3)
可以通过以下几种方法向其中添加更多元素ArrayBuffer:
// add one element nums += 4 // add multiple elements nums += 5 += 6 // add multiple elements from another collection nums ++= List(7, 8, 9)
可以ArrayBuffer使用-=和–=方法从中删除元素:
// remove one element nums -= 9 // remove multiple elements nums -= 7 -= 8 // remove multiple elements using another collection nums --= Array(5, 6)
其他方法
val a = ArrayBuffer(1, 2, 3) // ArrayBuffer(1, 2, 3) a.append(4) // ArrayBuffer(1, 2, 3, 4) a.append(5, 6) // ArrayBuffer(1, 2, 3, 4, 5, 6) a.appendAll(Seq(7,8)) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8) a.clear // ArrayBuffer() val a = ArrayBuffer(9, 10) // ArrayBuffer(9, 10) a.insert(0, 8) // ArrayBuffer(8, 9, 10) a.insertAll(0, Vector(4, 5, 6, 7)) // ArrayBuffer(4, 5, 6, 7, 8, 9, 10) a.prepend(3) // ArrayBuffer(3, 4, 5, 6, 7, 8, 9, 10) a.prepend(1, 2) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) a.prependAll(Array(0)) // ArrayBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g) a.remove(0) // ArrayBuffer(b, c, d, e, f, g) a.remove(2, 3) // ArrayBuffer(b, c, g) val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g) a.trimStart(2) // ArrayBuffer(c, d, e, f, g) a.trimEnd(2) // ArrayBuffer(c, d, e)
List类是线性的,不可变的序列。这意味着它是一个您无法修改的链表。每当您想添加或删除List元素时,都可以List从一个现存的中创建一个新元素List。
val ints = List(1, 2, 3) val names = List("Joel", "Chris", "Ed") //也可以根据需要声明List的类型,尽管通常不必这样做: val ints: List[Int] = List(1, 2, 3) val names: List[String] = List("Joel", "Chris", "Ed") 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
因为List是不可变的,所以不能向其中添加新元素。相反,您可以通过在现有元素之前或之后添加元素来创建新列表List。例如,鉴于此List:
val a = List(1,2,3) //可以像这样将元素放在前面List: val b = 0 +: a val e = List(-1, 0) ++: a
scala> var c = List(1,2,3,6,7) c: List[Int] = List(1, 2, 3, 6, 7) scala> c(1) res0: Int = 2 scala> c(4) res1: Int = 7
现在,IDE帮了我们很大的忙,但是记住这些方法名的一种方法是认为:字符表示序列所在的一侧,所以当您使用+:时,您知道列表需要在右侧,如下所示:
0+:a
同样,当您使用+时,您知道列表需要在左侧:
a:+4
因为List是一个链表类,所以不应该试图通过大列表的索引值来访问它们的元素。例如,如果列表中有一百万个元素,那么访问myList(999999)这样的元素将需要很长时间。如果要访问这样的元素,请改用Vector
或ArrayBuffer
。
you can also create the exact same list this way:
val list = 1 :: 2 :: 3 :: Nil
This works because a List is a singly-linked list that ends with the Nil
element.
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)
foldLeft:
scala> nums.foldLeft(0)(_ + _) res0: Int = 55 scala> nums.foldLeft(1)(_ * _) res1: Int = 3628800
Vector类是一个索引的,不变的序列。
可以通过以下几种方法创建Vector:
val nums = Vector(1, 2, 3, 4, 5) val strings = Vector("one", "two") val peeps = Vector( Person("Bert"), Person("Ernie"), Person("Grover") )
因为Vector是不可变的,所以不能向其中添加新元素。相反,您可以通过将元素添加或添加到现有之前创建新序列Vector。例如,鉴于此Vector
val a = Vector(1,2,3) //您可以添加以下元素: val b = a :+ 4 //还有这个: val b = a ++ Vector(4, 5) //也可以在前面加上这样的内容: val b = 0 +: a //还有这个: val b = Vector(-1, 0) ++: a
Scala具有可变和不变的Map类。这里,我们将展示如何使用可变的类。
import scala.collection.mutable.Map val states = Map( "AK" -> "Alaska", "IL" -> "Illinois", "KY" -> "Kentucky" )
states += ("AL" -> "Alabama") states += ("AR" -> "Arkansas", "AZ" -> "Arizona") states ++= Map("CA" -> "California", "CO" -> "Colorado")
tates -= "AR" states -= ("AL", "AZ") states --= List("AL", "AZ")
states("AK") = "Alaska, A Really Big State"
val ratings = Map( "Lady in the Water"-> 3.0, "Snakes on a Plane"-> 4.0, "You, Me and Dupree"-> 3.5 ) //for循环语法 for ((k,v) <- ratings) println(s"key: $k, value: $v") //将match表达式与foreach方法一起使用 ratings.foreach { case(movie, rating) => println(s"key: $movie, value: $rating") }
Scala具有可变和不变的Set类。在本课程中,我们将展示如何使用可变的类。
添加到一个可变Set用+=,++=和add方法。
set += 1 set += 2 += 3 set ++= Vector(4, 5)
以使用-=和–=方法从集合中删除元素
set -= 1 set -= (2, 3) set --= Array(4,5)
还有更多使用集合的方法,包括clear和remove:
元组可让您将元素的异构集合放入一个小容器中。元组可以包含两个到22个值,并且它们都可以是不同的类型。
val t = (11, "Eleven", new Person("Eleven")) t._1 t._2 t._3
或将元组字段分配给变量:
val (num, string, person) = (11, "Eleven", new Person("Eleven"))
首字母大写
val names = List("adam", "david", "frank") //yield 转换 val ucNames = for (name <- names) yield name.capitalize
Success! Each name in the new variable ucNames is capitalized.
val doubledNums = for (n <- nums) yield n * 2 val ucNames = for (name <- names) yield name.capitalize
val names = List("_adam", "_david", "_frank") val capNames = for (name <- names) yield { val nameWithoutUnderscore = name.drop(1) val capName = nameWithoutUnderscore.capitalize capName }
Scala has a concept of a match expression. In the most simple case you can use a match expression like a Java switch statement:
// i is an integer i match { case 1 => println("January") case 2 => println("February") case 3 => println("March") case 4 => println("April") case 5 => println("May") case 6 => println("June") case 7 => println("July") case 8 => println("August") case 9 => println("September") case 10 => println("October") case 11 => println("November") case 12 => println("December") // catch the default with a variable so you can print it case _ => println("Invalid month") }
match 表达式
val monthName = i match { case 1 => "January" case 2 => "February" case 3 => "March" case 4 => "April" case 5 => "May" case 6 => "June" case 7 => "July" case 8 => "August" case 9 => "September" case 10 => "October" case 11 => "November" case 12 => "December" case _ => "Invalid month" }
正常的函数
def convertBooleanToStringMessage(bool: Boolean): String = { if (bool) "true" else "false" }
match表达式
def convertBooleanToStringMessage(bool: Boolean): String = bool match { case true => "you said true" case false => "you said false" }
match表达式使您可以在单个case语句中处理多种情况。
为了说明这一点,假设您想像Perl编程语言那样对“布尔相等”进行求值:a 0或空白字符串求值为false,其他任何求值为true。这是您使用match表达式编写方法的方式,该表达式的计算结果为true和false:
example_1
def isTrue(a: Any) = a match { case 0 | "" => false case _ => true }
example_2
val evenOrOdd = i match { case 1 | 3 | 5 | 7 | 9 => println("odd") case 2 | 4 | 6 | 8 | 10 => println("even") case _ => println("some other number") }
example_3
cmd match { case "start" | "go" => println("starting") case "stop" | "quit" | "exit" => println("stopping") case _ => println("doing nothing") }
example_1
count match { case 1 => println("one, a lonely number") case x if x == 2 || x == 3 => println("two's company, three's a crowd") case x if x > 3 => println("4+, that's a party") case _ => println("i'm guessing your number is zero or less") }
提高可读性可在if表达式添加括号
count match { case 1 => println("one, a lonely number") case x if (x == 2 || x == 3) => println("two's company, three's a crowd") case x if (x > 3) => println("4+, that's a party") case _ => println("i'm guessing your number is zero or less") }
example_2
i match { case a if 0 to 9 contains a => println("0-9 range: " + a) case b if 10 to 19 contains b => println("10-19 range: " + b) case c if 20 to 29 contains c => println("20-29 range: " + c) case _ => println("Hmmm...") }
example_3
stock match { case x if (x.symbol == "XYZ" && x.price < 20) => buy(x) case x if (x.symbol == "XYZ" && x.price > 50) => sell(x) case x => doNothing(x) }
example_1
//这是一个Scala类,其构造函数定义了两个参数,firstName和lastName: class Person(var firstName: String, var lastName: String) //有了这个定义,您就可以Person像这样创建新的实例: val p = new Person("Bill", "Panner") //在类构造函数中定义参数会自动在该类中创建字段, //在本示例中,您可以像这样访问firstName和lastName字段: println(p.firstName + " " + p.lastName)
在此示例中,由于两个字段都被定义为var字段,因此它们也是可变的,这意味着可以更改它们。这是您如何更改它们:
scala> p.firstName = "William" p.firstName: String = William scala> p.lastName = "Bernheim" p.lastName: String = Bernheim
在第一个示例中,两个字段都被定义为var
字段:
class Person(var firstName: String, var lastName: String)
这使得这些字段可变。您还可以将它们定义为val
字段,这使它们不可变:
class Person(val firstName: String, val lastName: String)
在Scala中,类的主构造函数是以下各项的组合:
class Pizza (var crustSize: Int, var crustType: String) // a stock, like AAPL or GOOG class Stock(var symbol: String, var price: BigDecimal) // a network socket class Socket(val timeout: Int, val linger: Int) { override def toString = s"timeout: $timeout, linger: $linger" } class Address ( var street1: String, var street2: String, var city: String, var state: String )
您可以通过定义名为this
的方法来定义辅助类构造函数:
Here’s an example of a Pizza class that defines multiple constructors:
val DefaultCrustSize = 12 val DefaultCrustType = "THIN" // the primary constructor class Pizza (var crustSize: Int, var crustType: String) { // one-arg auxiliary constructor def this(crustSize: Int) { this(crustSize, DefaultCrustType) } // one-arg auxiliary constructor def this(crustType: String) { this(DefaultCrustSize, crustType) } // zero-arg auxiliary constructor def this() { this(DefaultCrustSize, DefaultCrustType) } override def toString = s"A $crustSize inch pizza with a $crustType crust" }
val p1 = new Pizza(DefaultCrustSize, DefaultCrustType) val p2 = new Pizza(DefaultCrustSize) val p3 = new Pizza(DefaultCrustType) val p4 = new Pizza
关于此示例,有两个重要说明:
Scala允许您提供构造函数参数的默认值。
example_1
class Socket(var timeout: Int, var linger: Int) { override def toString = s"timeout: $timeout, linger: $linger" }
class Socket(var timeout: Int = 2000, var linger: Int = 3000) { override def toString = s"timeout: $timeout, linger: $linger" }
通过提供参数的默认值,您现在可以Socket通过多种不同方式创建新的参数:
new Socket() new Socket(1000) new Socket(4000, 6000)
Named parameters(命名参数)
class Socket(var timeout: Int, var linger: Int) { override def toString = s"timeout: $timeout, linger: $linger" }
可以这样创建一个新的Socket:
val s = new Socket(timeout=2000, linger=3000)
该功能有时会派上用场,例如当所有类构造函数参数都具有相同的类型时(例如Int本示例中的参数)。例如,有些人发现此代码:
val s = new Socket(timeout=2000, linger=3000)
比以下代码更具可读性:
val s = new Socket(2000, 3000)
枚举是创建常量组的有用工具,这些常量组可以是星期几,一年中的月份,一副纸牌中的花色等,您可以使用一组相关的常量值。
sealed trait DayOfWeek case object Sunday extends DayOfWeek case object Monday extends DayOfWeek case object Tuesday extends DayOfWeek case object Wednesday extends DayOfWeek case object Thursday extends DayOfWeek case object Friday extends DayOfWeek
披萨相关的枚举
sealed trait Topping case object Cheese extends Topping case object Pepperoni extends Topping case object Sausage extends Topping case object Mushrooms extends Topping case object Onions extends Topping sealed trait CrustSize case object SmallCrustSize extends CrustSize case object MediumCrustSize extends CrustSize case object LargeCrustSize extends CrustSize sealed trait CrustType case object RegularCrustType extends CrustType case object ThinCrustType extends CrustType case object ThickCrustType extends CrustType
这些枚举为处理披萨馅料,大小和类型提供了一种不错的方法
给定这些枚举,我们可以定义一个Pizza这样的类:
class Pizza (var crustSize: CrustSize = MediumCrustSize, var crustType: CrustType = RegularCrustType) { // ArrayBuffer is a mutable sequence (list) val toppings = scala.collection.mutable.ArrayBuffer[Topping]() def addTopping(t: Topping): Unit = toppings += t def removeTopping(t: Topping): Unit = toppings -= t def removeAllToppings(): Unit = toppings.clear() }
一种使用Scala的trait方法就像原始的Java一样interface,在其中您可以为某些功能定义所需的接口,但是您没有实现任何行为。
example_1
trait TailWagger { def startTail(): Unit def stopTail(): Unit }
等效与Java
public interface TailWagger { public void startTail(); public void stopTail(); }
您可以编写一个扩展特征并实现如下方法的类:
class Dog extends TailWagger { // the implemented methods def startTail(): Unit = println("tail is wagging") def stopTail(): Unit = println("tail is stopped") }
请注意,无论哪种情况,都可以使用extends关键字创建一个扩展单个特征的类
extends和with用于从多个trait创建类:
example_1
trait Pet { def speak = println("Yo") // concrete implementation of a speak method def comeToMaster(): Unit // abstract }
class Dog(name: String) extends Pet { def comeToMaster(): Unit = println("Woo-hoo, I'm coming!") }
class Cat extends Pet { // override 'speak' override def speak(): Unit = println("meow") def comeToMaster(): Unit = println("That's not gonna happen.") }
trait Speaker { def speak(): String //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!" }
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") }
使用具有具体方法的特征可以做的一件非常有趣的事情是将它们动态混合到类中。例如
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实例时创建一个混合了这些特征的Dog实例
val d = new Dog("Fido") with TailWagger with Runner
此示例之所以有效,是因为定义了TailWagger和Runner特性中的所有方法(它们不是抽象的)。
Scala还具有类似于Java的抽象类的抽象类的概念。但是,由于特征是如此强大,因此您几乎不需要使用抽象类。实际上,仅在以下情况下才需要使用抽象类:
Scala trait不允许使用构造函数参数
// this won’t compile trait Animal(name: String)
因此,只要基本行为必须具有构造函数参数,就需要使用抽象类:
abstract class Animal(name: String)
注意,一个类只能扩展一个抽象类。
因为Java对Scala traits
一无所知,如果要从Java代码调用Scala代码,则需要使用抽象类而不是特质。
Abstract class syntax
abstract class Pet (name: String) { def speak(): Unit = println("Yo") // concrete implementation def comeToMaster(): Unit // abstract method }
class Dog(name: String) extends Pet(name) { override def speak() = println("Woof") def comeToMaster() = println("Here I come!") }
注意如何name传递
所有这些代码都类似于Java,因此我们将不对其进行详细说明。需要注意的一件事是如何将name构造函数参数从Dog类构造函数传递给Pet构造函数:
class Dog(name: String) extends Pet(name) {
记住,Pet声明name为构造函数参数:
abstract class Pet (name: String) { ...
因此,此示例说明如何将构造函数参数从Dog类传递给Pet抽象类。您可以验证此代码是否适用:
abstract class Pet (name: String) { def speak: Unit = println(s"My name is $name") } class Dog(name: String) extends Pet(name) val d = new Dog("Fido") d.speak
val ints = List(1,2,3) //要创建更大的列表时,还可以使用List的 range方法创建它们 val ints = List.range(1, 10)
example_1
//给出如下列表 val ints = List(1,2,3) //每个元素加倍来创建新列表ints val doubledInts = ints.map(_ * 2) //其中 _ * 2 //此代码是匿名函数 //也可以这样写: val doubledInts = ints.map((i: Int) => i * 2) val doubledInts = ints.map(i => i * 2) //等效于此Java代码 List ints = new ArrayList<>(Arrays.asList(1, 2, 3)); // the `map` process List doubledInts = ints.stream() .map(i -> i * 2) .collect(Collectors.toList()); //等效 val doubledInts = for (i <- ints) yield i * 2
_
Scala中的字符是通配符。您会看到它在几个不同的地方使用过。在这种情况下,这是一种简短的说法:“列表中的元素,” ints。
在函数式编程Simplified中,Alvin Alexander定义了一个纯函数,如下所示:
纯函数示例
正如您可能想象的那样,在给定纯函数定义的情况下,scala.math._包中的此类方法就是纯函数:
这些Scala String方法也是纯函数:
在Scala集合类的很多方法也作为纯粹的功能,包括drop,filter,和map。
不纯函数的例子
相反,以下功能不纯,因为它们违反了定义。
foreach集合类上的方法是不纯的,因为它仅用于其副作用,例如打印到STDOUT。
通常,不纯函数会执行以下一项或多项操作:
只需使用Scala的方法语法编写纯函数。这是一个纯函数,它将给定的输入值加倍:
def double(i: Int): Int = i * 2
Scala 可以将函数创建为变量,就像创建String和Int变量一样。
此功能有很多好处,其中最常见的是,它使您可以将函数作为参数传递给其他函数。在演示了map和filter方法时,您已经在本书的前面看到了:
val nums = (1 to 10).toList val doubles = nums.map(_ * 2) val lessThanFive = nums.filter(_ < 5)
匿名函数传递到map和中filter。
函数式编程就像编写一系列代数方程式一样,并且由于在代数中不使用空值,因此在FP中不使用空值。这就提出了一个有趣的问题:在Java / OOP代码中通常使用空值的情况下,您该怎么办?
Scala的解决方案是使用诸如Option / Some / None
类之类的构造。在本课中,我们将介绍这些技术。
def toInt(s: String): Int = { try { Integer.parseInt(s.trim) } catch { case e: Exception => 0 } }
此函数的思想是,如果字符串转换为整数,则返回转换后的Int,但如果转换失败,则返回0。这可能在某些方面是可以的,但它不是真的准确。例如,该方法可能接收到“0”,但它也可能接收到“foo”或“bar”或无限数量的其他字符串。这就产生了一个真正的问题:如何知道方法何时真正收到“0”,或者何时收到其他东西?答案是,用这种方法,没有办法知道。
Scala解决这个问题的方法是使用三个类,即Option、Some和None。Some类和None类是ABC的子类,因此解决方案如下:
def toInt(s: String): Option[Int] = { try { Some(Integer.parseInt(s.trim)) } catch { case e: Exception => None } }