赞
踩
Scala 是一种高级语言,其将【面向对象】和【函数式编程】相结合。
scala 代码和 java 代码不一样:编译器做了很多事情
java 中变量类型需要明文给出
scala 中可以对变量类型进行推断(防止运行期报错)
Scala的六大特性:
特性 | 描述 |
---|---|
SEANLESS JAVA INTEROP | Scala 运行在 JVM 上,因此 Java 代码和 Scala 代码可以相互混合,实现完全无缝集成 |
TYPE INFERENCE | Don’t work for the type system,Let the type system work for you |
CONCURRENCY & DISTRIBUTION | 对集合进行并行操作,使用 actors 进行并发和分发,以及 futures 的异步编程 |
TRAITS | 将 JAVA 风格接口的灵活性和类的强大功能结合起来 |
PATTERN MATCHING | 类似于"Switch",实现类的结构、序列、常量匹配 |
HIGER-ORDER FUNCTIONS | 函数是第一类值,函数可以是变量,也可以传递给其他函数 |
注释、命名(驼峰标识)、输出(简化为:println、print)等
只有两种变量类型:
// Scala 编译器会自动推断变量的类型,也可以显示声明:val a: Int = 1
val a = 1
var b = "bbxx"
Scala 中的数据类型都是对象,而不是基本类型,内置的数据类型有(Boolean
、Byte
、Short
、Int
、Long
、Float
、Double
、Char
、String
,其中 Int 和 Double 是默认的类型),以下是特殊类型的介绍。
对于大数字,可以使用 BigInt 和 BigDecimal 类型
举例:
var b = BigInt(12345667790)
var b = BigDecimal(123456.789)
使用双引号括住字符串,使用单引号括住字符:
val name = "Bill"
val c = 'a'
字符串合并(连接符号+
、字符串插值):
val firstName = "John" val mi = 'C' val lastName = "Doe" // 连接符号 + val name = firstName + " " + mi + " " + lastName // 字符串插值,符串前加上字母's',然后在字符串内的变量名前面方式一个 '$' 符号 val name = s"$firstName $mi $lastName" println(s"Name: $firstName $mi $lastName") // 可以将变量名用花括号包围 println(s"Name: ${firstName} ${mi} ${lastName}") //表达式也可以放入大括号中 println(s"1+1= ${1+1}") //使用三个双引号,可以创建多行字符串('|' 和 stripMargin 的作用是避免第一行后的行式缩进的。) val speech = """ FOUR score and | seven years ago | our fathers... """.stripMargin
其它:
【面向表达式编程】当编写的每个表达式都返回一个值时,这种风格被称为面向表达式的编程或 EOP。相反,不返回值的代码被称为语句,用于产生其他效果。
Scala 的if/else
控制结构类似于 Java 中的 if/else
。if 结构总是会返回一个结果,这个结果可以选择忽略,也可以将结果赋给一个变量,写作三元运算符:
val x = if (a < b) a else b
Scala 中有 match 表达式,类似于Java
中的 switch
语句。不过scala更强,支持任何数据类型、表达式可以有返回值以及强大的模式匹配(感觉有点类似于自然语言了)。
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 _ => "Unknown" } //match 表达式也可以返回值,可以将字符串结果赋给一个新值: val monthName = i match { case 1 => "January" case 2 => "February" case 3 => "March" case 4 => "April" case 5 => "May" case _ => "Invalid month" } // match 表达式支持在一个 case 语句中处理多个 case,下面的代码演示了将 0 或空字符串计算为 false // 可以看到这里输入参数 a 被定义为 Any 类型,这是所有 Scala 类的根类,就像 Java 中的 Object。 def isTrue(a: Any) = a match { case 0 | "" => false case _ => true } // 在 case 语句中使用 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") } 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 _ => println("Hmmm...") }
Scala 的异常捕获由 try/catch/finally
结构完成:
try {
writeToFile(text)
} catch {
case fnfe: FileNotFoundException => println(fnfe)
case ioe: IOException => println(ioe)
}
//finally 子句通常在需要关闭资源时使用
try {
} catch {
case foo: FooException => handleFooException(foo)
} finally {
}
Scala 中的for
循环用来迭代集合中的元素,通常这样写:
for (arg <- args) println(arg) for (i <- 0 to 5) println(i) for (i <- 0 to 10 by 2) println(i) // 在 for 循环中还可以使用 【yield】 关键字,从现有集合创建新集合 val x = for (i <- 1 to 5) yield i * 2 // 在 yield 关键字之后使用代码块可以解决复杂的创建问题 val capNames = for (name <- names) yield { val nameWithoutUnderscore = name.drop(1) val capName = nameWithoutUnderscore.capitalize capName } // 简约格式 val capNames = for (name <- names) yield name.drop(1).capitalize //还可以向 for 循环中添加守卫代码,实现元素的过滤操作: val fruit = for{ f <- fruits if f.length > 4 } yield f.length
// while
while (condition) {
}
// do while
do {
} while (condition)
函数的定义方式:
def sum(a: Int, b: Int): Int = a + b
// 不声明函数的返回类型
def sum(a: Int, b: Int) = a + b
// 函数的调用如下
val x = sum(1, 2)
// 创建一个 List
val ints = List(1, 2, 3)
val doubledInts = ints.map(_ * 2)
val doubledInts = ints.map((i: Int) => i * 2)
val doubledInts = ints.map(i => i * 2)
val x = ints.filter(_ > 5)
val x = ints.filter(_ % 2 == 0)
Map 和 Set 都有可变和不可变两种版本
Class | 描述 |
---|---|
ArrayBuffer | 有索引的、可变序列 |
List | 线性链表、不可变序列 |
Vector | 有索引的、不可变序列 |
Map | 键值对 |
Set | 无序的、去重集合 |
List 是一个线性的、不可变的序列,即一个无法修改的链表,想要添加或删除 List 元素时,都要从现有 List 中创建一个新 List。
创建方式:
val ints = List(1, 2, 3)
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 中添加或追加元素:
val a = List(1, 2, 3)
// 在 List 前面添加元素
val b = 0 +: a
val b = List(-1, 0) ++: a
Map 是由键和值组成的可迭代序列。Scala 中的 Map 类似于 Java 的 HashMap。
val ratings = Map (
"Lady in the water" -> 3.0,
"Snakes on a plane" -> 4.0,
"You, Me and Dupree" -> 3.5
)
可变的Map:
// 导入 import scala.collection.mutable.Map var states = collection.mutable.Map("AK" -> "Alaska") // 添加单个元素 states += ("AL" -> "Alabama") // 添加多个元素 states += ("AR" -> "Arkansas", "AZ" -> "Arizona") // 从另一个Map添加元素 states ++= Map("CA" -> "California", "CO" -> "Colorado") // 删除元素 states -= "AR" states -= ("AL", "AZ") states --= List("AL", "AZ") // 更新元素 states("AK") = "Alaska, A Really Big State" // 迭代元素 for((k, v) <- ratings) println(s"key: $k, value: $v") ratings.foreach { case(movie, rating) => println(s"key: $movie, value: $rating") } // 获取 Map 所有的 key states.keys // 获取 Map 所有的 Value states.values // 判断 Map 是否包含某个 Key states.contains(3) // 对 Map 的 value 进行转换操作 states.transform((k, v) => v.toUpperCase) // 对 Map 的 key 进行过滤 states.view.filterKeys(Set("AR", "AL")).toMap // 获取一个 Map 的前两个元素 states.take(2)
ArrayBuffer 是一个可变序列,可以使用它的方法来修改它的内容:
// 导入 import scala.collection.mutable.ArrayBuffer val ints = ArrayBuffer[Int]() val names = ArrayBuffer[String]() ints += 1 ints += 2 // 创建带有初试元素的 ArrayBuffer val nums = ArrayBuffer(1, 2, 3) nums += 5 += 6 nums ++= List(7, 8, 9) // 删除元素 nums -= 9 nums -= 7 -= 8 nums --= Array(5, 6)
ArrayBuffer 具有一些它自己的方法:
val a = ArrayBuffer(1, 2, 3) // 1, 2, 3 a.append(4) // 1, 2, 3, 4 a.appendAll(Seq(5, 6)) // 1, 2, 3, 4, 5, 6 a.clear() // val a = ArrayBuffer(9, 10) // 9, 10 a.insert(0, 8) // 8, 9, 10 a.insertAll(0, Vector(4, 5, 6, 7)) // 4, 5, 6, 7, 8, 9, 10 a.prepend(3) // 3, 4, 5, 6, 7, 8, 9, 10 a.prependAll(Array(0, 1, 2)) // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 val a = ArrayBuffer.range('a', 'h') // a, b, c, d, e, f, g a.remove(0) // b, c, d, e, f, g a.remove(2, 3) // b, c, g a.dropInPlace(2) // g a.dropRightInPlace(2)
Vector 是一个索引的、不可变序列,索引意味着可以通过索引值快速访问,其他方面与 List 类似:
val nums = Vector(1, 2, 3, 4, 5)
val strings = Vector("one", "two")
val peeps = Vector(
Person("Bert"),
Person("Ernie"),
Person("Grover")
)
// 由于 Vector 是不可变的,只能通过创建新序列的方式来向现有 Vector 追加元素:
val b = a :+ 4
val b = a ++ Vector(4, 5)
val b = 0 +: a
val b = Vector(-1, 0) ++: a
Set 是一个没有重复元素的可迭代集合。同时拥有可变和不可变的 Set 类,默认是不可变的。
可变Set:
import scala.collection.mutable.Set var set = scala.collection.mutabl.Set[Int]() set += 1 set +=2 +=3 set ++= Vector(4, 5) // add函数,元素添加成功返回true,否则false set.add(6) set.add(5) set -= 1 set -= (2, 3) set --= Array(4, 5) // 清空 set.clear() // 删除 set.remove()
元组中可以放入不同类型的元素(同一容器中存储异构数据),可以包含2个到22个值,所有值可以具有不同的类型:
val t = (11, 11.0, "Eleven")
对于元组变量,可以通过"_数字"来访问它的值:
t._1
t._2
t._3
也可以用模式匹配来将元组中的元素分配给各个变量:
val (symbol, price, volume) = ("AAPL", 123.45, 1101011)
当需要多次使用相同的元组时,可以声明一个专用的 case 类:
case class StockInfo(symbol: String, price: BigDecimal, volume: Long)
下面是一些常用的函数(这些函数都是返回一个新的集合,不改变原有集合)。
foreach:该函数对列表进行遍历操作,同时可以传入函数参数,对每个元素执行该函数
nums.foreach(println)
filter:该函数用于对列表元素进行过滤
nums.filter(_ < 4).foreach(println)
map:将传入的函数作用于列表中的每个元素,为每个元素返回一个新的、转换后的值
val doubles = nums.map(_ * 2)
head:返回第一个元素
nums.head
“foo”.head
tail:返回 head 元素之后的每一个元素
nums.tail
"foo".tail
take:将元素从集合中提取出来
nums.take(1)
nums.take(2)
takeWhile:将符合条件的元素从集合中提取出来
nums.takeWhile(_ < 5)
drop:将指定元素之外的元素从集合中提取出来
nums.drop(1)
nums.drop(5)
dropWhile:将条件之外的元素从集合中提取出
nums.dropWhile(_ < 5)
reduce:接收一个函数,并将函数作用于集合的后续元素
def add(x: Int, y: Int): Int = {
val theSum = x + y
println(s"received $x and $y, their sum is $theSum")
theSum
}
val a = List(1, 2, 3, 4)
a.reduce(add)
a.reduce(_ + _)
foldLeft:支持传入一个种子值,然后在其基础上进行计算操作
nums.foldLeft(0)(_ + _)
nums.foldLeft(1)(_ * _)
注意:不需要创建"get"和"set"方法来访问类中的字符
// 类定义
class Person(var firstName: String, var lastName: String) {
def printFullName() = println(s"$firstName $lastName")
}
// 类调用
val person = new Person("zhang", "san")
println(person.firstName)
person.lastName = "si"
person.printFullName()
在 Scala 中,一个类的主构造函数是以下的组合:
Scala 在定义类时,可以定义参数,这样编译器会产生默认构造函数:
class Person(var firstName: String, var lastName: String)
类构造函数中定义的参数会自动在类中创建字段,可以通过"."来访问:
Scala 允许为构造函数提供默认值:
class Socket(var timeout: Int = 2000, var linger: Int = 3000) {
override def toString = s"timeout: $timeout, linger: $linger"
}
// 默认值构造函数为参数提供了首选的默认值,但也可以根据自己的需要重写这些值,同时在调用构造函数时可用指定参数的名称
val s = new Socket(timeout = 1000, linger = 3000)
类主体中声明的字段类似于 Java 静态代码块,在类首次实例化时分配:
class Person(var firstName: String, var lastName: String) { println("the constructor begins") // 默认是 public var age = 0 private val HOME = System.getProperty("user.home") override def toString(): String = s"$firstName $lastName is $age years old" def printHome(): Unit = println(s"HOME = $HOME") def printFullName(): Unit = println(this) printHome() printFullName() println("you've reached the end of the constructor") }
上述代码的执行结果如下:
the constructor begins
HOME = “”
$firstName $lastName is $age years old
you've reached the end of the constructor
通过 this 关键字可以定义辅助构造函数:
val DefaultCrustSize = 12 val DefaultCrustType = "THIN" class Oizza (var crustSize: Int, var crustType: String) { def this(crustSize: Int) = { this(crustSize, DefaultCrustType) } def this(crustType: String) = { this(DefaultCrustSize, crustType) } def this() = { this(DefaultCrustSize, DefaultCrustType) } override def toString = s"A $crustSize inch pizza with a $crustType crust" }
枚举是创建小型常量组的工具,如一周中的几天、一年中的几个月等。
// 声明一个基本的 trait,然后根据需要使用 case 对象来扩展该 trait。
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
case object Saturday extends DayOfWeek
Scala 中的伴生对象即用 object 关键字声明的对象,并且与 class 有相同的名称。
classs Person { var name = "" } object Person { def apply(name: String): Person = { var p = new Person p.name = name p } } val p = Person.apply("Fred Flinstone") // 在调用时,实际过程如下: // 1. 输入 val p = Person("Fred") // 2. Scala 编译器发现在 Person 之前没有 new 关键字 // 3. 编译器在 Person 类的伴生对象中查找与输入的函数签名匹配的 apply 方法 // 4. 找到了就调用 apply 方法,否则返回编译器错误 val p = Person("Fred Flinstone") // 当然,在伴生对象中可以创建多个 apply 方法,从而提供多个构造函数 class Person { var name: Option[String] = None var age: Option[Int] = None override def toString = s"$name, $age" } object Person { def apply(name: Option[String]): Person = { var p = new Person p.name = name p } def apply(name: Option[String], age: Option[Int]): Person = { var p = new Person p.name = name p.age = age p } }
Scala 中还提供了反构造方法,可以从一个对象实例中返回传入的参数:
class Person(var name: String, var age: Int)
object Person {
def unapply(p: Person): String = s"${p.name}, ${p.age}"
}
val p = new Person("Lori", 29)
val result = Person.unapply(p)
样例类使用 case 关键字定义,它具有常规类的所有功能:
case class BaseballTeam(name: String, lastWorldSeriesWin: Int)
val cubs1908 = BaseballTeam("Chicago Cubs", 1908)
// 类中会生成 equals 和 hashcode 方法,用于比较对象
// 生成默认的 toString 方法
val cubs2016 = cubs1908.copy(lastWorldSeriesWin = 2016)
样例对象使用 case object 关键字定义:
用途1:创建枚举
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 case class Pizza { crustSize: CrustSize, crustType: CrustType, toppings: Seq[Topping]
用途2:为在其他对象之间传递的"消息"创建容器时使用(如 Akka actor 库)
case class StartSpeakingMessage(textToSpeak: String) case object StopSpeakingMessage case object PauseSpeakingMessage case object ResumeSpeakingMessage class Spark extends Actor { def receive = { case StartSpeakingMessage(textToSpeak) => // code to speak the text case StopSpeakingMessage => // code to stop speaking case PauseSpeakingMessage => // code to pause speaking case ResumeSpeakingMessage => // code to resume speaking } }
Scala 中 Traits 类似于 Java 中的接口,它可以将代码分解成小的模块化单元,实现对类的扩展。
trait Speaker {
def speak(): String // 这是一个抽象方法
}
trait TailWagger {
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}
//使用 extends 关键字和 with 关键字为 Dog 类扩展了 speak 方法
class Dog(name: String) extends Speaker with TailWagger {
def speak(): String = "Woof!"
}
定义一个 trait,它具有一个具体方法和一个抽象方法:
trait Pet {
def speak = println("Yo")
def comToMaster(): Unit
}
当一个类扩展一个 trait 时,每个抽象方法都必须实现:
class Dog(name: String) extends Pet {
def comToMaster(): Unit = println("Woo-hoo, I'm coming!")
}
对于具有具体方法的 trait,可以在创建实例时将其混合:
val d = new Dog("Fido") with TailWagger with Runner
类可以覆盖在 trait 中已定义的方法:
class Cat extends Pet {
override def speak(): Unit = println("meow")
def comToMaster(): Unit = println("That's not happen.")
}
Scala 中的抽象类使用较少,一般只在以下情况使用:
由于 Scala 的 trait 不支持构造函数参数,所以以下写法会报错:
// 报错
trait Animal(name: String)
当一些基类需要具有构造函数参数时,使用抽象类(注意:一个类只能扩展一个抽象类):
abstract class Animal(name: String)
// 一个类只能扩展一个抽象类
abstract class Pet(name: String) {
def speak(): Unit = println("Yo")
def comeToMaster(): Unit
}
class Dog(name: String) extends Pet(name) {
override def speak() = println("Woof")
def comToMaster() = println("Here I come!")
}
函数式编程:是一种强调只使用纯函数和不可变值编写应用程序的编程风格。
纯函数应该有以下特点:
Scala 中 "scala.math._package"中的如下方法都是纯函数:
当然,Scala 中有很多非纯函数,如 foreach:
非纯函数通常有以下特点:
函数可以作为变量进行传递,允许将函数作为参数传递给其他函数。
val doubles = nums.map(_ * 2)
在 Scala 中,没有 null 的存在,使用 Option/Some/Nothing 这样的结构进行处理。
//当三个字符串都转换为整数时,则返回一个包装在 Some 中的整数 // 当三个字符串中任何一个不能转换为整数时,则返回一个 None def toInt(s: String): Option[Int] = { try{ Some(Integer.parseInt(s.trim)) } catch { case e: Exception => None } } toInt(x) match { case Some(i) => println(i) case None => println("That didn't work.") } val y = for { a <- toInt(stringA) b <- toInt(stringB) c <- toInt(stringC) } yield a + b + c
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。