Scala 学习笔记-第十章特质

在scala中,特质既可以包含抽象方法也可以包含具体方法;比起java中的接口,它更像是类。

  1. 仅含有抽象方法的特质(看作接口)可以被一个扩展(extends),在子类中实现该抽象方法(不需要override);
  2. 含有具体实现的特质可以被“混入”(with)一个,相当于特质的具体方法被混入了那个类中,可以直接调用;
  3. 在构造对象时,特质可以被“混入”(with)这个对象,即在对象上可以调用该特质中的方法;
  4. 可以为一个类或对象添加多种特质,从最后一个开始处理;
  5. 特质可以被另一个特质扩展(即继承 extends),前者中的抽象方法可以被后者重写(需要加 abstract override);
  6. 特质可以包含大量的方法,它可以是具体的也可以是抽象的;当这个特质被添加(with)时,可以直接调用他的具体方法(第2点)或重写他的抽象方法(override)。
  7. 特质中的具体字段被简单地“添加到”子类中,并非继承。对于特质中的抽象字段,在使用该特质(具体的类或构造具体对象时),需要先提供字段。
  8. 特质没有构造器参数,要初始化需要在构造前提前定义
  9. 特质可以扩展类。特质的超类自动成为任何混入该特质的类的超类。
  10. 含有自身类型(this:=》type)的特质只能被混入这种类(给出类名)。含有结构类型(this: { def xxxx():xxx}=>)(给出类必须拥有的方法)的特质只能被混入拥有这种方法的类。

特质的几种用法

  • 当作接口使用的特质
    与接口作用相同,无具体实现的方法;可以在子类中实现log方法
trait Logged{
    def log(msg:String) //这是一个抽象方法
    }
class ConsoleLogger extends Logger{//ConsoleLogger是特质Logger的子类
    def log(msg:String){println(msg)} //注意这里不需要写override
    }
使用with来添加额外的特质,一个类只可以拥有一个超类,但可以拥有任意特质
class ConsoleLogger extends Logger with Cloneable with Serializable

  • 带有具体实现的特质
trait ConsoleLogger{
    def log(msg:String){ println (msg)}
    }
    //Account是超类,ConsoleLogger是添加的特质
class SavingAccount extends Account with ConsoleLogger{
    def withdraw(amount:Double){        //直接调用特质中的具体方法
        if(amount>balance)  log("Insufficient funds") 
        else balance-=amount
}
  • 带有特质的对象
trait Logged{
    def log(msg:String){ }
    }
trait ConsoleLogger extends Logged{
    override def log(msg:String){ println(msg)}
    }
class SavingAccount extends Account with Logged{
    def withdraw(amount:Double){
        if(amount>balance) log("Insufficient funds")
        else ...
        }
        ...
    }

val acct=new SavingAccount with ConsoleLogger
//特质ConsoleLogger 扩展了 特质Loggerd,并重写了方法log.
//单纯地调用SavingAccount时,log方法不会做任何事。(Logged中的log空)
//但在构造对象时混入了ConsoleLogger,改变了这个对象里的log方法。
  • 叠加在一起的特质
    特质可以互相扩展,可以为类或对象添加多个互相调用的特质。
    调用顺序一般来说从最后一个特质开始处理。具体的可以参考构造顺序

  • 在特质中重写抽象方法

trait Logger{
    def log(msg:String)
}
trait TimestampLogger extends Logger{
    override def log(msg:String){ 
        super.log(new java.util.Date()+" "+msg)
    }
}
//这里试图像类扩展类一样重写抽象方法,但super.log无法编译,因为他是抽象的(Logger.log是抽象的,所以我们需要加上abstract)
trait TimestampLogger extends Logger{
    abstract override def log(msg:String){ 
        super.log(new java.util.Date()+" "+msg)
    }
}
//在调用时我们仍需要混入一个具体的log方法。
  • 当做富接口使用的特质
trait Logger{
    def log(msg:String)
    def info(msg:String){log("INFO: "+msg)}//在一个具体方法里调用了上面的抽象方法
    def warn(msg:String){log("WARN: "+msg)}
    def severe(msg:String){log("SEVERE: "+msg)}
}
class SavingAccount extends Account with Logger{
    def withdraw(amount:Double){
        if (amount>balance) severe("Insufficient funds")
        else ...
        }
        ...
        override def log(msg:String){println(msg);}
    }
//在这里,info.war.severe三个方法均依赖log抽象方法实现;  
  • 特质中的具体字段与抽象字段

给出了初始值的字段就是具体字段,当特质被添加至一个类或对象里时,该字段被装备到这个类或这个对象里。

trait ShortLogger extends logged{
    val maxLength:Int
    override def log(msg:String){
        super.log(
            if(msg.length<=maxLength) msg.substring(0,maxLength-3)+"...")
        }
            ...
}   

特质中未被初始化的字段就是抽象字段,当在一个具体的类或对象中使用该特质时,需要提供字段

val acc=new SavingAccount with ConsoleLogger with ShortLogger{ val maxLength=20}


class SavingAccount extends Account with ConsoleLogger with ShortLogger{
    val maxLength=20
    ...
}
  • 特质构造顺序
    超类-特质(从左到右)-每个特质中先构造父特质-所有特质完毕后子类
class SavingAccount extends Account with TimestampLogger with ShortLogger
//构造顺序:Account-Logged-TimestampLogger-ShortLogger-SavingAccount

  • 初始化特质中的字段
    特质没有构造器参数
trait FileLogger extends Logger{
    val filename:String
    val out=new PrintStream(filename)
    def log(msg:String){ out.println(msg); out.flush()}
}
val acct = new{
    val filename="myapp.log"
    }with SavingAccount with FileLogger
  • 扩展类的特质
trait LoggedException extends Exception with Logged{
    def log() { log( getMessage() ) } //log方法调用了从Exception继承下来的getMessage()方法
}

class UnhappyException extends LoggedException{ 
    override def getMessage()="arggh!"
}
//UnhappyException扩展至一个特质,LoggedException的超类自动编程UnhappyException的超类,所以它可以调用getMessage().
  • 自身类型
trait LoggedException extends Logged{
    this:Exception=>
        def log() { log(getMessage() ) }
}
//这个特质没有扩展Exception类,但有一个自身类型Exception.所以他只能被混入Exception的子类。在该特质中调用getMessage()是合法的,因为这个特质this一定是Exception。
trait LoggedException extends Logged{
    this: { def getMessage(): String } =>
        def log()  { log( getMessage() )}
}
//这个特质可以被混入任何拥有getMessage()方法的类

总结:
特质的加入使得scala的面向对象的应用空前灵活,我们可以很有逻辑的增加方法或修改方法,而不至于使得结构紊乱。希望在以后的练习中可以更加熟悉特质的用法。

你可能感兴趣的:(学习笔记,scala,class,特质)