准备这个Chapter讲文件和正则表达式,但该内容按照其他语言安排一般都是在最后几章节,所以暂时先忽略该章节,后面会补上。
转入正题,如果有两个类Student和Employee,它们都有name这个属性,如果要同时扩展这两个基类该如何做,主要问题是name属性如何处理?如果保留两个,那么访问时到底访问的是哪个呢?如果只保留一个,那么保留哪一个?该问题就是菱形问题。画一个形象的图形如下:
Scala通过提供特质解决这个问题,特质可以同时拥有抽象方法和具体方法,而类可以实现多个特质。
特质完全可以像Java接口那样工作。
特质中未被实现的方法默认是抽象方法,不需要用abstract
申明。
重写特质抽象方法时不需要给出override
关键字。
特质不止一个,可以使用with
关键字来添加额外的特质。
Scala类只能有一个超类,可以有任意数量的特质。
trait ConsoleLogger {
def log(msg: String) { println(msg) } // 具体方法
}
class Account {
protected var balance = 0.0
}
class SavingsAccount extends Account with ConsoleLogger {
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds")
else balance -= amount
}
}
with
关键字。 trait Logged {
def log(msg: String) { }
}
trait ConsoleLogger extends Logged {
override def log(msg: String) { println(msg) }
}
class Account {
protected var balance = 0.0
}
class SavingsAccount extends Account with Logged {
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds")
else balance -= amount
}
}
object Main extends App {
val acct1 = new SavingsAccount
acct1.withdraw(100) // 没有任何信息会打印出来
println("Overdrawing acct2");
val acct2 = new SavingsAccount with ConsoleLogger // 添加特质进来
acct2.withdraw(100) // 会打印信息
}
**个人理解:**添加多个特质要研究的问题实际是,添加的这几个特质哪个先执行?即执行的顺序,执行完的结果是要传给另一个特质进行下一步处理,这里有点像linux中pipe的概念。
trait Logged {
def log(msg: String) { }
}
trait ConsoleLogger extends Logged {
override def log(msg: String) { println(msg) }
}
trait TimestampLogger extends Logged { // 给消息添加时间戳
override def log(msg: String) {
super.log(new java.util.Date() + " " + msg)
}
}
trait ShortLogger extends Logged { // 截断过长的信息
val maxLength = 15
override def log(msg: String) {
super.log(
if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...")
}
}
class Account {
protected var balance = 0.0
}
class SavingsAccount extends Account with Logged {
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds") // 调用特质中的log方法
else balance -= amount
}
}
object Main extends App {
val acct1 = new SavingsAccount with ConsoleLogger with
TimestampLogger with ShortLogger
val acct2 = new SavingsAccount with ConsoleLogger with
ShortLogger with TimestampLogger
acct1.withdraw(100)
acct2.withdraw(100)
}
**个人理解:**这节就是想区分用类来扩展特质是不需要加override,而特质扩展特质就要加override。
abstract
和override
关键字。 trait Logger {
def log(msg: String) // 抽象的方法
}
trait TimestampLogger extends Logger {
abstract override def log(msg: String) {
super.log(new java.util.Date() + " " + msg)
}
}
trait ConsoleLogger extends Logger {
override def log(msg: String) { println(msg) } // 这里说明abstract是可以省略的
}
***个人理解:**这节无非是上面的基本应用,train中可以根据不同的应用场景来定义多个同一功能的方法。
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) }
}
个人理解:这节主要目的是知道特质中可以有具体的字段,具体的意思就是经过初始化的字段。并且类在扩展特质后,具体字段不是被继承的,而是直接被加入到子类中。说白了就是在类中又拷贝了该字段的一个副本,类中可以直接使用。
**个人理解:**具体字段可以直接使用,那么如果是抽象字段该怎么办?这节给出特质中如果有抽象字段,类中使用就必须要重新定义该字段,并且不需要使用override关键字,这在第二小节已经明确。
trait Logger {
val maxLength: Int // 抽象的字段
}
class TimestampLogger extends Logger {
val maxLength: Int = 20 // 不需要写override
}
**个人理解:**和类一样,特质也有构造器,同样特质中也存在构造执行顺序的问题。记住特质的构造顺序对写程序是很有帮助的。
特质的构造器执行顺序:
个人理解:首先知道特质是不能有构造参数,对某种定制的特质来说这就是一个问题,那么该如何解决?这小结给给出了两种解决方案:提前定义和懒值。
特质不能有构造参数,但都有一个无参的构造器,缺少构造器参数这是特质和类之间唯一的技术差别,特质可以具备类的所有特性。
提前定义的方法如下:
trait Logger {
def log(msg: String)
}
// 使用提前定义定义变量filename
trait FileLogger extends Logger {
val filename: String
val out = new PrintWriter(filename)
def log(msg: String) { out.println(msg); out.flush() }
}
class Account {
protected var balance = 0.0
}
abstract class SavingsAccount extends Account with Logger {
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds")
else balance -= amount
}
}
object Main extends App {
val acct = new { // new之后就是提前定义块,在构造器执行前就定义好
val filename = "myapp.log"
} with SavingsAccount with FileLogger
acct.withdraw(100)
}
trait Logger {
def log(msg: String)
}
// 使用懒值
trait FileLogger2 extends Logger {
val filename: String
lazy val out = new PrintWriter(filename)
def log(msg: String) { out.println(msg); out.flush() }
}
class Account {
protected var balance = 0.0
}
abstract class SavingsAccount extends Account with Logger {
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds")
else balance -= amount
}
}
object Main extends App {
val acct2 = new SavingsAccount with FileLogger2 {
val filename = "myapp2.log"
}
acct2.withdraw(100)
}
个人理解:标题有点绕,其实意思就是某个特质扩展了类,这种情况scala会有哪些特殊的地方需要注意。
trait Logged {
def log(msg: String) { }
}
trait LoggedException extends Exception with Logged {
def log() { log(getMessage()) }
}
这里LoggedException扩展了Exception类,并调用了从Exception超类继承下来的getMessage()方法。
**个人理解:**除上面可以扩展某个类的方法之外,scala还提供另外一种方式,不用扩展某个类,直接把某个类作为自身的一个类型,从而达到同样的目的。
this: 类型 =>
,特质定义自身类型后,只能被混入指定类型的子类。比如: trait Logged {
def log(msg: String) { }
}
trait LoggedException extends Logged {
this: Exception => // 特质不是扩展Exception类,而是有一个自身类型Exception
def log() { log(getMessage()) }
}
trait Logged {
def log(msg: String) { }
}
trait LoggedException extends Logged {
this: { def getMessage() : String } =>
def log() { log(getMessage()) }
}
【待续】
内容来源于网络,如有侵权,请联系作者删除!