目录
摘要
特质概念,定义特质,抽象方法,抽象属性,实现多个特质,继承特质,带有特质的对象,特质构造顺序,特质与抽象类
特质
概念
- 特质是一些字段和行为的集合
- 特质可以同时拥有抽象方法和具体方法
- 一个类可以实现多个特质
- 特质不能有带参数的构造函数。
定义一个特质
trait Logger {
//字段
val brand: String
val minLength: Int
//行为
//抽象方法
def log(msg: String)
//带有具体实现
def echo(msg: String) {
println(msg)
}
//依赖抽象方法
def info(msg: String) = log("INFO" + msg)
}
- 特质使用关键字 trait 声明
- 特质中可以有抽象方法和抽象属性,且复写时无需使用关键字
override
- 特质中的方法可以互相依赖,即使是依赖抽象方法
实现特质
实现特质使用关键字 extends
,之后如果需要实现多个特质时,则使用关键字 with
class ConsoleLogger extends Logger with Cloneable with Serializable {
//实现抽象方法
def log(msg: String): Unit = println(msg)
//实现抽象属性
val minLength: Int = 1
}
以上代码实际内部是将 Logger with Cloneable with Serializable
看作了一个整体类进行了继承。
继承特质
trait Logger {
def show(msg: String) {}
def log(msg: String)
}
trait OutputLogger extends Logger{
override def show(msg: String): Unit = println(msg)
//重写抽象方法
abstract override def log(msg: String): Unit = {
super.log(msg)
}
}
- 特质也可以继承特质
- 如果需要在重写的抽象方法中调用父类特质的抽象方法,必须将方法本身声明为抽象的,即
abstract
。
带有特质的对象
可以为单个对象添加特质
trait Logger {
def show(msg: String) {}
}
trait OutputLogger extends Logger{
override def show(msg: String): Unit = println(msg)
}
class LoggerExample extends Logger {}
val logger1 = new LoggerExample
logger1.say("hello")
//为单个对象添加特质
val logger2 = new LoggerExample with OutputLogger
logger2.say("hello world") //hello world
特质的构造顺序
特质构造顺序遵循以下原则
- 首先调用超类的构造器
- 特质构造器在超类构造器之后,类构造器之前执行
- 特质由左至右被构造
- 每个特质中,父特质先被构造
- 如果多个特质有同一个父特质,且父特质已被构造了,则不会被再次构造
- 所有特质构造完毕,子类才被构造
例
有如下类
class SavingAccount extends Account with FileLogger with ShortLogger
则构造顺序为
- Account
- Logger
- FileLogger
- ShortLogger
- SavingAccount
在 ShortLogger 中调用 super 会执行 FileLogger 的方法
叠加在一起的特质
一般情况下,特质总是从最后一个开始向前执行
trait Logger {
def show(msg: String) {}
}
trait TimeStampLogger extends Logger {
override def show(msg: String): Unit = {
super.show(new Date() + " " + msg)
}
}
trait ShortLogger extends Logger {
val maxLength = 15
override def show(msg: String): Unit = {
super.show(
if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "..."
)
}
}
class LoggerExample extends Logger {}
val logger3 = new LoggerExample with OutputLogger with TimeStampLogger with ShortLogger
logger3.show("hello world logger3") //Mon Feb 16 11:46:06 CST 2015 hello world ...
val logger4 = new LoggerExample with OutputLogger with ShortLogger with TimeStampLogger
logger4.show("hello world logger4") //Mon Feb 16 1...
什么时候应该使用特质而不是抽象类?
如果你想定义一个类似接口的类型,你可能会在特质和抽象类之间难以取舍。这两种形式都可以让你定义一个类型的一些行为,并要求继承者定义一些其他行为。一些经验法则:
- 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
- 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。
- 如果需要类从 Java类继承,使用抽象类。
- 如果需要考虑效率问题,使用抽象类。Java的动态绑定机制决定了直接方法要快于接口方法。而特质最终是被编译成接口。