学习笔记摘抄于Twitter scala文档:Twitter.github.io
为了方便,本文中类的定义代码和调用代码同放一处;
实际上,代码的调用需要写在程序入口(main方法)
next:Scala集合
一、apply方法
当类或对象有一个主要用途的时候,apply方法为你提供了一个很好的语法糖。
class Foo {}
object FooMaker {
def apply() = new Foo
}
val newFoo = FooMaker() //实际上调用的是FooMaker中的apply方法
在class中定义apply方法,通过新建该class的对象可进行调用
class Bar {
def apply() = 0
}
val bar = new Bar
val res = bar() //return 0; 实际上调用了对象的apply方法
二、单例对象Object
Objects are used to hold single instances of a class. Often used for factories.
定义一个Object
object Timer {
var count = 0
def currentCount(): Long = {
count += 1
count
}
}
单例对象可以和类具有相同的名称,此时该对象也被称为“伴生对象”。我们通常将伴生对象作为工厂使用
class Bar(foo: String)
object Bar {
def apply(foo: String) = new Bar(foo)
}
val bar = Bar("Hello world")
三、Functions are Objects(函数即是对象)
函数是一些特质trait的集合。具体来说,具有一个参数的函数是Function1特质的一个实例。
这个特征定义了apply()语法糖,让你调用一个对象时就像你在调用一个函数。
object addOne extends Function1[Int, Int] {
def apply(m: Int): Int = m + 1
}
addOne(2) //返回3
Function1特质的源码:
package scala
@scala.annotation.implicitNotFound("No implicit view available from ${T1} => ${R}.")
trait Function1[@scala.specialized -T1, @scala.specialized +R] extends scala.AnyRef {
def apply(v1 : T1) : R
@scala.annotation.unspecialized
def compose[A](g : scala.Function1[A, T1]) : scala.Function1[A, R] = { /* compiled code */ }
@scala.annotation.unspecialized
def andThen[A](g : scala.Function1[R, A]) : scala.Function1[T1, A] = { /* compiled code */ }
override def toString() : java.lang.String = { /* compiled code */ }
}
- 这个Function特质集合下标从0开始一直到22
- apply语法糖有助于统一对象和函数式编程的二重性。你可以传递类,并把它们当做函数使用,而函数本质上是类的实例
- 在类中定义的方法是方法而不是函数
类也可以扩展Function,这些类的实例可以使用()调用。
class AddOne extends Function1[Int, Int] {
def apply(m: Int): Int = m + 1
}
val plusOne = new AddOne()
plusOne(2) //直接用对象名+参数 实际上调用的是对象的apply方法
可以使用更直观快捷的extends (Int => Int)代替extends Function1[Int, Int]
class AddOne extends (Int => Int) {
def apply(m: Int): Int = m + 1
}
四、包(package)
package com.twitter.example
值和函数不能在类或单例对象之外定义。单例对象是组织静态函数(static function)的有效工具。
package com.twitter.example
object colorHolder {
val BLUE = "Blue"
val RED = "Red"
}
println("the color is: " + com.twitter.example.colorHolder.BLUE)
五、模式匹配(Pattern Matching)
Matching on values
val times = 1
times match {
case 1 => "one"
case 2 => "two"
case i if i == 3 => "three"
case _ => "some other number"
}
- 在最后一行指令中的_是一个通配符;它保证了我们可以处理所有的情况。
否则当传进一个不能被匹配的数字的时候,你将获得一个运行时错误。 - 按顺序匹配,匹配到后就返回
Matching on type
def bigger(o: Any): Any = {
o match {
case i: Int if i < 0 => i - 1
case i: Int => i + 1
case d: Double if d < 0.0 => d - 0.1
case d: Double => d + 0.1
case text: String => text + "s"
case _ => "unknown"
}
}
Matching on class members
def calcType(calc: Calculator) = calc match {
case _ if calc.brand == "HP" && calc.model == "20B" => "financial"
case _ if calc.brand == "HP" && calc.model == "48G" => "scientific"
case _ if calc.brand == "HP" && calc.model == "30B" => "business"
case _ => "unknown"
}
(⊙o⊙)哦,太痛苦了。幸好Scala提供了一些应对这种情况的有效工具--样例类
六、样例类(Case Classes)
case classes are used to conveniently store and match on the contents of a
class. You can construct them without using new.
- 帮你实现一个该类的伴生对象,并在伴生对象中提供apply方法,让你不用new关键字就能构造出相应的对象
- 在伴生对象中提供unapply方法让模式匹配可以工作
- 样本类参数列表中的所有参数隐式地获得了val前缀,因此它们被当作字段维护
- 添加toString、hashCode、equals、copy的“自然”实现
case class Calculator(brand: String, model: String)
val hp20b = Calculator("HP", "20b")
val hp20B = Calculator("HP", "20b")
def calcType(calc: Calculator) = calc match {
case Calculator("HP", "20B") => "financial"
case Calculator("HP", "48G") => "scientific"
case Calculator("HP", "30B") => "business"
case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
}
最后一句也可以这样写
case Calculator(_, _) => "Calculator of unknown type"
case _ => "Calculator of unknown type"
case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)
七、异常(Exceptions)
Scala中的异常可以在try-catch-finally语法中通过模式匹配使用
try {
remoteCalculatorService.add(1, 2)
} catch {
case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailable. should have kept your trusty HP.")
case e: IoException => log.error(e, "io error")
} finally {
remoteCalculatorService.close()
}