标签:快学scala
class ConsoleLogger extends Logger with Cloneable with Serialiable
由上,Logger ith Cloneable with Serialiable首先是一个整体,然后再由类来扩展。
2. 特质的方法并不需要一定是抽象的:
trait ConsoleLogger{
def log(msg: String) { println(msg)}
}
class SavingAccount extends Account with ConsoleLogger {
def withdraw(amount: Double){
if(amount > balance) log("Insufficient funds")
else balance -= amount
}
} //scala与java的区别,SavingAccount从ConsoleLogger特质得到了一个具体的log方法,java的接口是不可能做到的。scala中是ConsoleLogger的功能被混入了SavingAccount类
class SavingsAccount extends Account with FileLogger with ShortLogger
//构造顺序是: Account(超类)Looger(第一个特质的父特质)FileLogger(第一个特质)ShortLogger(第二个特质,它的父特质Logger已被构造),SavingsAccount(类)
val acct = new SavingsAccount with FileLogger("myapp.log") //错误:特质不能使用构造器参数
FileLogger构造器先于子类构造器执行,在轮到子类之前,FileLogger的构造器就会抛出一个空指针异常。解决方法是 “提前定义”:
val acct = new{
val filename = "myapp.log"
}with SavingsAccount with FileLogger //提前定义发生在常规的构造序列之前,在FileLogger被构造时,filename已经是初始化过的了
this: 类型 =>
//当特质以这一定义开始时,它便被混入指定类型的子类
trait LoggedException extends Logged{
this: Exception =>
def log() { log(getMessage) }
} //该特质并不扩展Exception类,而是有一个自身类型Exception,这意味着,它只能被混入Exception的子类。
如果把特质混入一个不符合自身类型要求的类,就会报错
10.1 java.awt.Rectangle类有两个很有用的方法translate和grow,但可惜的是像java.awt.geom.Ellipse2D这样的类没有。在Scala中,你可以解决掉这个问题。定义一个RenctangleLike特质,加入具体的translate和grow方法。提供任何你需要用来实现的抽象方法,以便你可以像如下代码这样混入该特质:
val egg = new java.awt.geom.Ellipse2D.Double(5,10,20,30) with RectangleLike
egg.translate(10,-10)
egg.grow(10,20)
import java.awt.geom.Ellipse2D
trait RectangleLike{ //使用自身类型使得trait可以操作x,y
this:Ellipse2D.Double =>
def translate(x: Double, y: Double){
this.x = x
this.y = y
}
def grow(x: Double, y: Double){
this.x += x
this.y += y
}
}
object Test extends App{
val egg = new Ellipse2D.Double(5,10,20,30) with RectangleLike
println("x = " + egg.getX + " y = " + egg.getY)
egg.translate(10, -10)
println("x = " + egg.getX + " y = " + egg.getY)
egg.grow(10, 20)
println("x = " + egg.getX + " y = " + egg.getY)
}
10.2 把scala.math.Ordered[Point]混入java.awt.Point的方式,定义OrderedPoint类。按辞典编辑方式排序,也就是说,如果
x<x'或者x=x'且y<y'则(x,y)<(x',y')
import scala.math.Ordered
import java.awt.Point
class OrderedPoint(x: Int, y: Int) extends Point(x,y) with Ordered[Point]{ //Point(x,y)写全
def compare(that: Point): Int = {
if(this.x <= that.x){
if(this.x == that.x) {
if(this.y < that.y) -1
else if(this.y > that.y) 1
else 0
}else -1
}else -1
}
override def toString = "[%d, %d]".format(getX(), getY())
}
val x1 = new OrderedPoint(1,1)
val x2 = new OrderedPoint(1,-1)
val x3 = new OrderedPoint(2,1)
println(x1 < x2)
println(x1 >= x3)
10.4 提供一个CryptoLogger类,将日志消息以凯撒密码加密。缺省情况下密匙为3,不过使用者也可以重写它。提供缺省密匙和-3作为密匙是的使用示例
trait Logger {
def log(msg: String) = {}
}
trait ConsoleLogger extends Logger {
override def log(msg: String) = Console.println(msg)
}
trait CaesarLogger extends Logger {
val shift: Int = 3
override def log(msg: String) = {
super.log((for(x <- msg) yield (x + shift).toChar).mkString)
// more elegant
super.log(msg.map(_ + shift).map(_.toChar).mkString)
// speedup but less elegant
super.log(msg.map((x : Char) => (x + shift).toChar).mkString)
}
}
class Sample extends Logger {
def doSomeWork() = {
log("Some Log Message")
}
}
val x = new Sample with ConsoleLogger
x.doSomeWork
val y = new Sample with ConsoleLogger with CaesarLogger
y.doSomeWork
val z = new { override val shift = -3} with Sample with ConsoleLogger with CaesarLogger
z.doSomeWork
10.5 JavaBean规范里有一种提法叫做属性变更监听器(property change listener),这是bean用来通知其属性变更的标准方式。PropertyChangeSupport类对于任何想要支持属性变更通知其属性变更监听器的bean而言是个便捷的超类。但可惜已有其他超类的类—比如JComponent—必须重新实现相应的方法。将PropertyChangeSupport重新实现为一个特质,然后将它混入到java.awt.Point类中
import java.awt.Point
import java.beans.PropertyChangeSupport
trait PropertyChange extends PropertyChangeSupport
val p = new Point() with PropertyChange
10.6 在Java AWT类库中,我们有一个Container类,一个可以用于各种组件的Component子类。举例来说,Button是一个Component,但Panel是Container。这是一个运转中的组合模式。Swing有JComponent和JContainer,但如果你仔细看的话,你会发现一些奇怪的细节。尽管把其他组件添加到比如JButton中毫无意义,JComponent依然扩展自Container。Swing的设计者们理想情况下应该会更倾向于图10-4中的设计。但在Java中那是不可能的。请解释这是为什么?Scala中如何用特质来设计出这样的效果?
Java只能单继承,JContainer不能同时继承自Container和JComponent。Scala可以通过特质解决这个问题.
10.7 市面上有不下数十种关于Scala特质的教程,用的都是些"在叫的狗"啦,"讲哲学的青蛙"啦之类的傻乎乎的例子。阅读和理解这些机巧的继承层级很乏味且对于理解问题没什么帮助,但自己设计一套继承层级就不同了,会很有启发。做一个你自己的关于特质的继承层级,要求体现出叠加在一起的特质,具体的和抽象的方法,以及具体的和抽象的字段
trait Fly{
def fly(){
println("flying")
}
def flywithnowing()
}
trait Walk{
def walk(){
println("walk")
}
}
class Bird{
var name: String= _
}
class BlueBird extends Bird with Fly with Walk{
def flywithnowing(){
println("BlueBird flywithnowing")
}
}
object Test extends App{
val b = new BlueBird()
b.walk()
b.flywithnowing()
b.fly()
}
10.8 在java.io类库中,你可以通过BufferedInputStream修饰器来给输入流增加缓冲机制。用特质来重新实现缓冲。简单起见,重写read方法
import java.io.{InputStream, FileInputStream}
trait Buffering{
this: InputStream =>
val BUF_SIZE: Int = 5
private val buf = new Array[Byte](BUF_SIZE)
private var bufsize: Int = 0
private var pos: Int = 0
override def read(): Int = {
if(pos >= bufsize){
bufsize = this.read(buf, 0, BUF_SIZE)
if(bufsize > 0) -1
pos = 0
}
pos += 1
buf(pos - 1)
}
}
val f = new FileInputStream("test.txt") with Buffering
for( i <- 1 to 10) println(f.read())
10.9 使用本章的日志生成器特质,给前一个练习中的方案增加日志功能,要求体现缓冲的效果
import java.io.{InputStream, FileInputStream}
trait Logger {
def log(msg: String)
}
trait NoneLogger extends Logger {
def log(msg: String) = {}
}
trait PrintLogger extends Logger {
def log(msg: String) = println(msg)
}
trait Buffering {
this: InputStream with Logger =>
val BUF_SIZE: Int = 5
private val buf = new Array[Byte](BUF_SIZE)
private var bufsize: Int = 0
private var pos: Int = 0
override def read(): Int = {
if (pos >= bufsize) {
bufsize = this.read(buf, 0, BUF_SIZE)
log("buffered %d bytes: %s".format(bufsize, buf.mkString(", ")))
if (bufsize > 0) -1
pos = 0
}
pos += 1
buf(pos-1)
}
}
val f = new FileInputStream("test.txt") with Buffering with PrintLogger
for(i <- 1 to 10) println(f.read())
10.10 实现一个IterableInputStream类,扩展java.io.InputStream并混入Iterable[Byte]特质
trait IterableInputStream extends java.io.InputStream with Iterable[Byte]{
class InputStreamIterator(outer: IterableInputStream) extends Iterator[Byte]{
def hasNext: Boolean = outer.available() > 0
def next: Byte = outer.read().toByte
}
override def iterator: Iterator[Byte] = new InputStreamIterator(this)
// override def read(): Int = 0
}
val f = new java.io.FileInputStream("test.txt") with IterableInputStream
for(b <- f) println(b.toChar)