根据documents记录一些零碎的知识点,学习一定程度后再总结一下。
1 basic
1.1 Variables and Values
Variables和Values分别用var和val表示,他们之间的区别是var可以重复赋值,而val赋值后不可改变,相当于const。
val x = 1+1
var y: Int = 1 + 1
y = 3
变量的type类似c++11中auto会自己推断,同时也可以显式地标明:
val x: Int = 1+1
用{}将表达式圈起来就是一个blocks,相当于一个作用域,最后的一行表达式的输出就是blocks的输出:
println({
val x = 1 + 1
x + 1
}) // 3
1.2 function
带参数的表达式就是函数。
匿名函数:
()括号内是参数:
(x: Int) => x + 1
named函数:
val getAns = () => 4
单个参数:
val add = (x: Int) => x + 1
println(add(1))
多个参数
val add = (x: Int, y: Int) => x + y
=>右边就是带有参数的表达式
高阶函数
高阶函数:其他函数做参数或者是其结果的函数。
c++只能调用函数指针,来实现上述形式。
def apply(f: Int => String, v: Int) = f(v)
1.3 method
method结构: def关键字,方法名,多个参数列表或者没有参数列表,返回类型,body
def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
如果返回类型是Unit,那么就表示viod,只是scala每个表达式必须返回一个值,所以用Unit,表示没有什么信息。
1.4 Classes
类的构成:
类名,构造参数
class Greeter(prefix: String, suffix: String) {
def greet(name: String): Unit =
println(prefix + name + suffix)
}
val greeter = new Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!
默认构造函数
class Point(var x: Int = 0, var y: Int = 0)
val origin = new Point // x and y are both set to 0
val point1 = new Point(1)
println(point1.x) // prints 1
默认构造下只想手动赋值非第一个的参数,需要指明参数名称
class Point(var x: Int = 0, var y: Int = 0)
val point2 = new Point(y=2)
println(point2.y) // prints 2
private,将变量与类外隔离;与c++类似可以用def方法读取私有变量
private[A]会增加可见范围到A。
val修饰的变量默认为公有的,没有val即为私有;
class Point {
private var _x = 0
private var _y = 0
private val bound = 100
def x = _x
def x_= (newValue: Int): Unit = {
if (newValue < bound) _x = newValue else printWarning
}
def y = _y
def y_= (newValue: Int): Unit = {
if (newValue < bound) _y = newValue else printWarning
}
private def printWarning = println("WARNING: Out of bounds")
}
抽象类:
abstract class Element { //abstract表示抽象类
def contents: Array[String] //没有函数体表示声明抽象方法
}
无参函数和属性, 统一访问模式
abstract class Element {
def contents: Array[String] //组合Array[]
def height: Int = contents.length //无参函数
def width: Int = if (height == 0) 0 else contents(0).length
}
//等同于
abstract class Element {
def contents: Array[String]
val height = contents.length //属性
val width = if (height == 0) 0 else contents(0).length
}
1.5 case
该类在类前加case关键字,不可修改并且之间可以比较大小
case 类最基本形式:
case calss + 名字 + 参数列表
实例化不需要new,因为该类有默认的apply方法构造。
case class Message(sender: String, recipient: String, body: String)//变量类型为val public
val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?")//实例化该类不需要new关键字
message1.sender = "travis@washington.us" // val是immutable的
case实例的比较:比较基于结构而不是引用,虽然两者明显指向的对象不一样,但是仍然可以进行比较。
case class Message(sender: String, recipient: String, body: String)
val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val messagesAreTheSame = message2 == message3 // true
case的复制:
case class Message(sender: String, recipient: String, body: String)
val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg")
val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr")
message5.sender // travis@washington.us
message5.recipient // claire@bourgogne.fr
message5.body // "Me zo o komz gant ma amezeg"
因为可变和不可变的区别,所以使用val相对case来说更安全。
1.6 Traits
traits相当于Java接口,但是功能更加强大,因为他还可以定义属性和方法。
与generic class和abstract method结合:
trait Iterator[A] {
def hasNext: Boolean
def next(): A
}
因为scala的类仅继承单一父类,但是特征却可以多重继承。
extend和with
extend, implicitly inherit the trait’s superclass
class Frog extends Philosophical { //mix in Philosophical trait
override def toString = "green"//override子类覆写父类方法
}
with, 需要显式的指明超类
class Animal
trait HasLegs
class Frog extends Animal with Philosophical with HasLegs {//继承Animal,并mix in两个traits
override def toString = "green"
}
1.7 type
类型体系:
类型转换顺序:
编译时Scala自动对应到Java原始类型,提高运行效率。Unit对应java的void
隐式转换
implicit def foo(s:String):Int = Integer.parseInt(s) // 需要时把String->Int
def add(a:Int, b:Int) = a+b
add("100",8) // 108, 先把"100"隐式转换为100
1.8 各种关键字
1.8.1 require
def f(n:Int) = { require(n!=0, "n can't be zero"); 1.0/n }
1.8.2 application
不带命令行参数的简化main方法:
object app1 extends Application {
println("hello world")
}
1.8.3 for循环
for (i <- 0 to n) foo(i) // 包含n,即Range(0,1,2,...,n,n+1)
for (i <- 0 until n) foo(i) // 不包含n,即Range(0,1,2,3,...,n)
1.8.4 reduceLeft /foldLeft /scanLeft
def fac(n: Int) = 1 to n reduceLeft(_*_)
fac(5) // 5*4*3*2 = 120
相当于:
((((1*2)*3)*4)*5)
def sum(L: Seq[Int]) = L.foldLeft(0)(_ + _)//累加
def multiply(L: Seq[Int]) = L.foldLeft(1)(_ * _)//累积
List(1,2,3,4,5).scanLeft(0)(_+_) // (0,1,3,6,10,15)
相当于:
(0,(0+1),(0+1+2),(0+1+2+3),(0+1+2+3+4),(0+1+2+3+4+5))
注:
@ (z /: List(a, b, c))(op) 相当于 op(op(op(z, a), b), c)
简单来说:加法用0,乘法用1
@ (List(a, b, c) :\ z) (op) equals op(a, op(b, op(c, z)))
此处省略一万字。。。。。。。。。。。。。。。
1.8 pattern matching
1.8.1 定义
value match expression,起作用类似if else
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many" //类似case中的default
}
matchTest(3) // many
matchTest(1) // one
1.8.2 与case class
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
notification match {
case Email(email, _, _) if importantPeopleInfo.contains(email) =>
"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("867-5309", "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))
println(showImportantNotification(someVoiceRecording, importantPeopleInfo))
println(showImportantNotification(importantEmail, importantPeopleInfo))
println(showImportantNotification(importantSms, importantPeopleInfo))
1.8.3 Matching on type only
abstract class Device
case class Phone(model: String) extends Device{
def screenOff = "Turning screen off"
}
case class Computer(model: String) extends Device {
def screenSaverOn = "Turning screen saver on..."
}
def goIdle(device: Device) = device match {
case p: Phone => p.screenOff
case c: Computer => c.screenSaverOn
}
1.8.4 Sealed classes
sealed abstract class Furniture
case class Couch() extends Furniture
case class Chair() extends Furniture
def findPlaceToSit(piece: Furniture): String = piece match {
case a: Couch => "Lie on the couch"
case b: Chair => "Sit on the chair"//不需要catch all了
}
1.9 Regular Expression Patterns
任何string对象都可以转换成正则表达式:
import scala.util.matching.Regex
val numberPattern: Regex = "[0-9]".r//在string后加.r
1.10 For Comprehensions
for (enumerators) yield e
enumerators:可以是generator,或者是filter;
满足要求的就可以将e加入到一个list中
def foo(n: Int, v: Int) =
for (i <- 0 until n;
j <- i until n if i + j == v)
yield (i, j)
foo(10, 10) foreach {
case (i, j) =>
print(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5)
}
1.11 Variances
Covariance
如果有class List[+A],则表示该类是covariance。那么如果A是B的subtype,那么List[A]也是List[B]的subtype。
那么就可以在有List[B]的情况下用List[A]替换掉。
Contravariance
如果有class List[-A],那么如果A是B的subtype,那么List[B]是List[A]的subtype。
Invariance
正常情况下,A和B的关系不影响其List之间的关系
type bounds
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
[P <: Pet]表示,该类的参数必须是Pet的subtype
1.12 Abstract Types
抽象的type意味着具体的type需要根据实现来定义:
trait Buffer {
type T
val element: T//element的类型是T
}
抽象类extends了traits:
SeqBuffer 类的新类型必须是
abstract class SeqBuffer extends Buffer {
type U
type T <: Seq[U]
def length = element.length
}
Compound Types
def cloneAndReset(obj: Cloneable with Resetable): Cloneable = {
//...
}
Polymorphic Methods
与generic class相似,[A]表示某个type
def listOfDuplicates[A](x: A, length: Int): List[A] = {
if (length < 1)
Nil
else
x :: listOfDuplicates(x, length - 1)
}
println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3)
println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La)
Local Type Inference
类型推断,编译器可以根据表达式进行类型推断,而不用每次都显式地标明。
Polymorphic Methods或者generic class中也可以进行类型推断。
object InferenceTest1 extends App {
val x = 1 + 2 * 3 // the type of x is Int
val y = x.toString() // the type of y is String
def succ(x: Int) = x + 1 // method succ returns Int values
}
但是如果返回值是一个递归调用方法,那么类型推断将失效。
以下情况类型推断将会出错:
object InferenceTest4 {
var obj = null
obj = new Object()
}
By-name Parameters与By-value Parameters
def calculate(input: => Int) = input * 37//By-name
By-name在不需要的时候不会evaluated,
By-value 在一开始就会且仅会evaluated一次。
2 OO
2.1 Generic Classes
将type作为一个参数的类
类名后加[A],将type A作为参数传入
class Stack[A] {//以下操作都只对type A有用
private var elements: List[A] = Nil
def push(x: A) { elements = x :: elements }
def peek: A = elements.head
def pop(): A = {
val currentTop = peek
elements = elements.tail
currentTop
}
}
如果type下面还有subtype,那么对于subtype同样适用
class Fruit
class Apple extends Fruit
class Banana extends Fruit
val stack = new Stack[Fruit]
val apple = new Apple
val banana = new Banana
stack.push(apple)
stack.push(banana)
2.2 singleton object
用object代替class
现在,sum可以通过import test .Blah.sum导入了
package test
object Blah {
def sum(l: List[Int]): Int = l.sum
}
Scala不能定义静态成员, 所以用Singleton对象来达到同样的目的
import ChecksumAccumulator.calculate
object Summer {
def main(args: Array[String]) {
for (arg <args)
println(arg +": "+ calculate(arg))
}
}
2.3 继承
超类的私有成员不会被子类继承.
抽象成员, 需要被子类实现(implement)
一般成员, 可用被子类重写(override)
重写方法和属性,两者不能重名,但是可以转换。
参数化属性
class ArrayElement( val contents: Array[String] ) extends Element //实现属性
final成员
加在method前,表示成员函数禁止被任何子类override
加在class前,表示该类禁止被任何子类继承
2.4 Extractor Objects
用法
import scala.util.Random
object CustomerID {
def apply(name: String) = s"$name--${Random.nextLong}"//apply方法创造一个实例
def unapply(customerID: String): Option[String] = {
val name = customerID.split("--").head
if (name.nonEmpty) Some(name) else None
}//unapply做了反操作,提取出name
}
val customer1ID = CustomerID("Sukyoung") // 创造了实例
val customer2ID = CustomerID("Suhal") //等价于val name = CustomerID.unapply(customer2ID).get
val CustomerID(name) = customer2ID//用于赋值
customer1ID match {
case CustomerID(name) => println(name) // prints Sukyoung
case _ => println("Could not extract a CustomerID")
}
返回值
仅仅是测试,返回 Boolen即可;
返回一个子值,返回Option[T];
返回多个子值,返回Option[(T1,…,Tn)]。
None:Option的两个子类之一,另一个是Some,用于安全的函数返回值
数据结构
数组
可变的同类对象序列, 适用于OO场景
val greetStrings = new Array[String](3) //greetStrings为val, 但是内部的数组值是可变的
greetStrings(0) = "Hello" //greetStrings.apply(0),所以用()
greetStrings(1) = ", "
greetStrings(2) = "world!\n"
//简单初始化
val numNames = Array("zero", "one", "two")
对应可变的:
val ab = collection.mutable.ArrayBuffer[Int]()
ab += (1,3,5,7)
ab ++= List(9,11) // ArrayBuffer(1, 3, 5, 7, 9, 11)
ab toArray // Array (1, 3, 5, 7, 9, 11)
ab clear // ArrayBuffer()
List
List为不可变对象序列
对于List最常用的操作符为::, cons, 把新的elem放到list最前端
:::, 两个list的合并
Nil:长度为0的List
Queues
import scala.collection.immutable.Queue //不可变Queue
val has1 = empty.enqueue(1)//入队
val has123 = has1.enqueue(List(2, 3)) //添加多个元素
val (element, has23) = has123.dequeue //取出头元素,返回两个值, 头元素和剩下的queue
import scala.collection.mutable.Queue //可变Queue
queue += "a" //添加单个
queue ++= List("b", "c") //添加多个
queue.dequeue //取出头元素, 只返回一个值
Stacks
import scala.collection.mutable.Stack
Tuple
tuple和list一样是不可变的, 不同是, list中的elem必须是同一种类型, 但tuple中可以包含不同类型的elem
val pair = (99, "Luftballons")
println(pair._1) //从1开始的!
Set和Map
Scala需要兼顾OO和FP, 所以需要提供mutable和immutable版本
默认是Immutable, 如果需要使用mutable版本, 需要在使用前显示的引用
Sorted Set and Map
基于红黑树实现的有序的Set和Map