Pragmatic Scala:Create Expressive, Concise, and Scalable Applications
目录
From Java to Scala
- Tuple
- 使用t._1访问Tuple t的第一个成员(下标从1开始)
- val (firstName, lastName) = getPersonInfo(1)
- 变参
- def max(values: Int*) = values.foldLeft(values(0)) { Math.max }
- 将Array做变参展开:max(numbers: _*)(这估计又是编译期的语法糖?)
- 隐式参数(caller决定默认值,而非默认参数中callee决定)
- callee:def connectToNetwork(user: String)(implicit wifi: Wifi) { ... } //有点‘上下文’的意味?
- caller:implicit def officeNetwork = new Wifi("office-network") //What the fuck;
- Strings与多行字符串(从Perl中学来的?)
- scala.runtime.RichString?
- """ .... .... ... """
- val message = s"A discount of $discount% has been applied" //字符串插值,注意这里的s前缀
- Sensible Defaults
- 如果你不感兴趣可以不用捕获异常(将checked异常转换为runtime?)
- ()和.都是可选的(英文的代码风格)
- 运算符重载
- Scala Surprises for the Java Eyes
- 赋值的结果类型是Unit,所以不允许 a = b = 0; 连续赋值
- == 其实是 equals()
- 行尾;可选
- 避免显示return(Why?Scala的习俗?)
- Default Access Modifier
- 略
Objects
- 主构造器:
- class Person(val firstName: String, val lastName: String) { ... }
- var position: String = _ //转换为一个position() getter,和一个setter:position_=()
- 辅助:def this (firstName: String, lastName: String, pos: String) { this(firstName, lastName) ... }
- override def ...
- class Person(val firstName: String, val lastName: String) { ... }
- JavaBean
- import scala.beans.BeanProperty
- @BeanProperty var position: String = _ //修饰前缀
- 类型别名
- type Cop = PoliceOfficer //这是从ML/Haskell学习的吗
- Extending a Class
- class Car(override val id: Int, override val year: Int, var fuelLevel: Int) extends Vehicle(id, year) { //这里的override参数修饰很奇怪
- Parameterized Types(模板?)
- def echo2[T1, T2](input1: T1, input2: T2) = ...
- Singletons and Companions
- class Marker private(val color: String) { ... }
- object Marker { //这个同名的Marker类称为伴侣对象?
- object MarkerFactory { ... } //单例,object类
- Scala没有类static成员
- class Marker private(val color: String) { ... }
- 枚举
- object Currency extends Enumeration {
- type Currency = Value
- val CNY, GBP, INR, JPY, NOK, PLN, SEK, USD = Value
- object Currency extends Enumeration {
- Package对象
- 与package同名的object类?
Types
- Scala不支持ArrayList[Int]赋值给ArrayList类型(相当于ArrayList[Nothing])的引用
- Any(superclass of all types)
- AnyVal
- AnyRef(直接映射到Java Object类):notify(), wait(), finalize()
- Nothing:bottomest type(可以理解为所有类型的并?T1 | T2 | ...)
- Optional[T]:Some[T] or None
- Either[T, S]
- Covariance and Contravariance(协变与逆变)
- 之所以不允许List<T>转换为List<Any>,是因为这样一来,就有可能串改List中的元素,导致所有元素不再是同一类型,==> covariance,反之允许List<Any>到List<T>则为contravariance
- 允许covariance:def playWithPets[T <: Pet](pets: Array[T]) = ...
- 这里的意思是说,只要输入容器中的元素类型是Pet的子类,函数都能够处理(即不要求元素是同一子类类型)
- Pet定义了T的‘上界’(类型层次中最上面的是Any)
- 支持contravariance(下界):
- def copyPets[S, D >: S](fromPets: Array[S], toPets: Array[D]) = ... //看来逆变用于定义from/to类型约束
- class MyList[+T] //允许协变;
允许T或其基类- -T
- 隐式类型转换
- Implicit Functions(相当于C++里面的operator T类型转换函数): 在一个object类中implicit def即可
- Implicit Classes
- Value类*
- 使用隐式转换(有点意思,Ruby里的Helper类扩展方法)
- implicit class Interpolator(val context: StringContext) extends AnyVal { ...
Function Values and Closures
- (args, ...) => ...
- Currying:def foo(a: Int)(b: Int)(c:Int) = {}
- val total = (0 /: arr) { (sum, elem) => sum + elem }
- val total = (0 /: arr) { _ + _ } (占位参数)
- val largest = (Integer.MIN_VALUE /: arr) { Math.max }
- 部分应用的函数(不同于Currying?)
- val logWithDateBound = log(date, _ : String) //内部封装了一个新的函数对象
- Closures
- var product = 1; loopThrough(5) { product *= _ }
- Execute Around Method
Traits
- 实例trait:var a = new A() with T;
- abstract override:子trait实现基trait里定义的抽象方法接口?
Collections
- Set、List、Map
- immutable
- ::: list prepend操作
- method /:() is equivalent to foldLeft() and \:() to foldRight()
- val total2 = (0 /: feeds) { (total, feed) => total + feed.length }
- 操作符重载:
- unary_+() unary_-() unary_~() unary_!()
- for生成器:for([pattern <- generator; definition*]+; filter*) [yield] expression
模式匹配与正则表达式
- input match { ... case _ => ... } //注意,Map()里使用->表示key-value映射
- case List("red", "blue", _*) => //忽略剩余元素
- case List("apple", "orange", otherFruits @ _*) => //命名剩余元素
- case (a: Int, b: Int) =>
- case msg : Int if (msg > 1000000) =>
- case this.max => //不加this.就将创建一个模式变量?
- case `max` => //引用scope内的常量?
- case MAX => //使用大写,这也不会创建模式变量
- case类(这有点像Clojure里面的多重派发)
- 必须extends同一个trait?
- 调用时传递case类实例需要加(),否则是一个Function0 trait对象
- Extractors与正则表达式
- unapply()(输入就是match,返回Extractor的实例)
- RE作为提取器:
- val GoogStock = """^GOOG:(\d*\.\d+)""".r
- case GoogStock(price) => ...
- _
- var msg : String = _ //初始化为默认null
- 函数命名中作为运算符的前缀
- val square = Math.pow(_: Int, 2)
处理异常
- Java的异常catch的顺序敏感,而Scala使用模式匹配的风格(它该不会是先捕获异常基类,然后再进行模式匹配的吧?)
递归
- TCO
- Java字节码:invokeSpecial --> goto?
- @scala.annotation.tailrec //只能处理直接递归;
- Trampoline:处理间接递归
- import scala.util.control.TailCalls._
- 用done()和tailcall()封装原来的f<-->g互相递归调用 //看起来很神奇啊?
惰性求值与并行集合
- lazy val perform = expensiveComputation()
- You can turn a strict collection into lazy using the view() method
- 有点数据库中‘物化视图’的感觉
- Stream
- #:: (::的lazy版本)
- println(generate(25).takeWhile{ _ < 40 }.force)
- 并行集合
- par()与seq()
- timeSample { cities => (cities.par map getWeatherData).toList }
- Furthermore, you shouldn't use parallel collections if the operations are nonassociative.
- par()与seq()
Actors
- import akka.actor._
- class HollywoodActor() extends Actor {
- def receive = { case message => ... //隐式参数???
- val system = ActorSystem("sample")
- val depp = system.actorOf(Props[HollywoodActor]) //见鬼,几乎每一个特性都有新的语法(这一点上远没有Erlang纯粹)
- depp ! "Wonka"
- 使用建议
- Rely more on stateless actors instead of stateful actors
- Keep the processing in the receive() method really fast
- Ensure the messages passed between actors are immutable objects
- As much as possible, avoid using ask()(2路同步通信?)
Java互操作
- 避免Java中的名字与Scala关键字冲突:val theYield2 = investment.`yield`()
创建应用
- import scala.io.Source => Source.fromFile("ReadingFile.scala").foreach { print }
- val xmlFragment = <symbols> ...
- var symbolNodes = xmlFragment \ "symbol" //XPath选择器?
- case <units>{numberOfUnits}</units> => ...
- val data = scala.io.Source.fromURL(url).mkString
单元测试
- JUnit:略
- Using ScalaTest
- "a list" should "be empty on create" in new EmptyArrayList { list.size should be (0) } //变态的语法
- Using Mockito
- "score" should "return 0 for an empty word" in { withWordScorer { wordScorer => wordScorer.score("") should be (0) } }