特质
为什么没有多重继承
Scala提供“特质”而非接口。特质可以同时拥有抽象方法和具体方法,而类可以实现多个特质。
当做接口使用的特质
trait Logger {
def long(msg: String) //这是个抽象方法
}
子类:
class ConsoleLogger extends Logger { //使用extends关键字
def long(msg: String) {println(msg)} //无需写override
}
带有具体实现的特质
trait ConsoleLogger {
def log(msg: String) {println(msg)}
}
使用:
class SavingsAccount extends Account with ConsoleLogger {
def withdraw (amount: Double) {
if (amount > balance) log("Insufficient funds")
else balance -= amount
}
}
当做富接口使用的特质
特质可以包含大量工具方法,而这些工具方法可以依赖一些抽象方法来实现。
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("SEVAERE : " + msg)}
}
调用:
class SavingsAccount extends Account with Logger {
def withdraw(amount: Double) {
if (amount > balance) severe("Insufficient funds")
else ...
}
...
override def log(msg: String) {println(msg)}
}
特质中的具体字段
特质中的字段可以是具体的,也可以是抽象的。如果你给出了初始值,那么字段就是具体的。
trait ShortLogger extends Logged {
val maxLength = 15 //具体的字段
...
}
class SavingsAccount extends Account with ConsoleLogger with ShortLogger {
var interest = 0.0
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds")
else ...
}
}
class Account {
var balance = 0.0
}
那么,在SavingsAccount中就会有三个实际的字段存在。
var balance = 0.0
var interest = 0.0
var maxLength = 15
你可以把具体的特质字段当做是针对使用该特质的类的”装配指令”。任何通过这种方式被混入的字段都自动成为该类自己的字段。
特质中的抽象字段
特质中未被初始化的字段在具体的子类中必须被重写。
trait ShortLogger extends Logged {
val maxLength: Int //抽象字段
override def log(msg: String) {
super.log(
if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...")
)
}
}
在具体的类中使用该特质中,你必须提供maxLength字段:
class SavingsAccount extends Account with ConsoleLogger with ShortLogger {
val maxLength = 20 //不需要写override
...
}
特质构造顺序
举例:
trait FileLogger extends Logger {
val out = new PrintWriter(app.log) //这是特质构造器的一部分
out.println("# " + new Date().toString()) //同样是特质构造器的一部分
def log(msg: String) {out.println(msg); out.flush}
}
class SavingsAccount extends Account with FileLogger with ShortLogger
构造器按照如下顺序执行:
- Account(超类)
- Logger(第一个特质的父特质)
- FileLogger(第一个特质)
- ShortLogger(第二个特质)。注意它的父特质Logger已被构造。
- SavingsAccount(类)
总结如下:
1. 首先调用超类的构造器。
2. 特质构造器在超类构造器之后、类构造器之前执行。
3. 特质从左到右被构造。
4. 每个特质当中,父特质先被构造。
5. 如果多个特质公有一个父特质,而那个父特质已经被构造,则不会被再次构造。
6. 所有特质构造完毕,子类被构造。
初始化特质中的字段
特质不能有构造器参数。
缺少构造器参数是特质和类之间唯一的技术差别。
自身类型
this: 类型 =>
例子:
trait LoggedException extends Logged {
this: Exception =>
def log() {log (getMessage())}
}
背后发生了什么
1.只有抽象方法的特质被简单的变成了一个Java接口:
trait Logger {
def log(msg: String)
}
====>
public interface Logger {
void log(String msg);
}
2.如果特质有具体的方法,Scala会帮我们创建出一个伴生类,该伴生类用静态方法存放特质的方法。
trait ConsoleLogger extends Logger {
def log(msg: String) {println(msg)}
}
====>
public interface ConsoleLogger extends Logger { //生成的java接口
void log(String msg);
}
public class ConsoleLogger$class { //生成的Java伴生类
public static void log(ConsoleLogger self, String msg) {
println(msg)
}
}
3.当某个类实现该特质时,字段会被自动加入。
trait ShortLogger extends Logger {
val maxLength = 15 //具体的字段
}
====>
public interface ShortLogger extends Logger {
public abstract int maxLength();
public abstract void weird_prefix$maxLength_$eq(int);
...
}
本文深入探讨Scala中的特质概念,包括特质如何替代多重继承、特质的具体与抽象字段、构造顺序及自身类型等关键特性。
273

被折叠的 条评论
为什么被折叠?



