特质
- 所有的面向对象的语言都不允许直接的多重继承,因为会出现“deadly diamond of death”问题。 Scala 提供了特质(trait) ,特质可以同时拥有抽象方法和具体方法,一个类可以实现多个特质。
- 特质中没有实现的方法就是抽象方法。类通过 extends 继承特质,通过 with 可以继承多个特质。
- 特质的方法可以抽象也可以不抽象。
object Scala05_Trait01 {
def main(args: Array[String]): Unit = {
val log:Logger = new Console
log.log("Println MSG ...")
log.printLog()
}
}
/**
* 特质更像是Java中的抽象类
*/
trait Logger {
def log(msg: String)
def printLog():Unit={
println("Println Log。。。")
}
}
// 所有的 java 接口都可以当做 Scala 特质使用
class Console extends Logger with Cloneable with Serializable {
def log(msg: String) {
println(msg)
}
}
运行结果:
Println MSG ...
Println Log。。。
1 带有具体实现的特质
- 和 Java 中的接口不太一样的是特质中的方法并不一定是抽象的,也可以有默认实现。
object Scala01_Trait {
def main(args: Array[String]): Unit = {
val stu:Person001 = new Student001
stu.speak()
stu.walk()
}
}
trait Person001{
//具体的方法:
def speak()={
println("说话")
}
//抽象的方法
def walk()
}
class Student001 extends Person001{
def walk():Unit = {
println("六亲不认的步伐")
}
}
说话
六亲不认的步伐
2 带有特质的对象,动态混入
- Java的借口可以作为特质混入
- 可以混入多个特质
object Scala02_Trait {
def main(args: Array[String]): Unit = {
//动态混入特质
val mysqlDB = new MySQLDB001 with DB001
mysqlDB.insert(21)
mysqlDB.delete(10)
}
}
trait DB001{
def insert(id:Int)={
println("插入数据id=" + id)
}
}
class MySQLDB001{
def delete(id:Int)={
println("删除数据id=" + id)
}
}
插入数据id=21
删除数据id=10
3 叠加在一起的特质
- 构建对象的同时如果混入多个特质, 那么特质声明顺序从左到右,方法执行顺序从右到左
object Scala03_Trait {
def main(args: Array[String]): Unit = {
// 叠加在一起的特质叠加顺序是从右向左再向上
// 叠加在一起的特质加载顺序是从左到右的
var mysql = new MySQL002 with Data002 with File002 with DB002
mysql.insert(2)
}
}
trait Operate002 {
println("Oper002...")
def insert( id : Int )
}
trait Data002 extends Operate002 {
println("Data002...")
def insert( id : Int ) {
println("插入数据【"+id+ "】 " )
}
}
trait File002 extends Data002 {
println("File002...")
override def insert( id : Int ) {
print("向文件" )
super.insert(id)
}
}
trait DB002 extends Data002 {
println("DB002...")
override def insert( id : Int ) {
print("向数据库" )
super.insert(id)
}
}
class DataBase002{
println("DataBase002.。。")
}
class MySQL002 extends DataBase002{
}
DataBase002.。。
Oper002...
Data002...
File002...
DB002...
向数据库向文件插入数据【2】
- 此时会发现 super 关键字并不是我们理解的调用父特质的方法,而是调用下一个特质的同名方法(如果存在的话)
- 如果想要调用具体特质的方法,可以指定: super[Data].xxx(…).其中的泛型必须是该特质的直接超类类型
4 在特质中重写抽象方法
- 如果调用父特质的抽象方法,为了保证一定要有方法实现,可以让当前特质重写父特质中的抽象方法。 防止混入特质时出现错误。
object Scala04_Trait {
def main(args: Array[String]): Unit = {
val mysql004 = new MySQL004 with DB004 with Data004
mysql004.insert(10)
/*
加入 abstract override修饰词,下面这种语法不再被编译通过了
val mysql04 = new MySQL004 with Data004
*/
}
}
trait Operate004 {
//抽象insert方法
def insert( id : Int )
}
trait Data004 extends Operate004 {
// abstract override def insert( id : Int )
override abstract def insert( id : Int ) {
super.insert(id) // 调用父特质的抽象方法,那么在实际使用时,没有方法的具
//体实现,无法编译通过,为了避免这种情况的发生。可重写抽象方法,这样在使用时,
//就必须实现该方法,就不会出现错误了,参考特质 DB
}
}
trait DB004 extends Operate004 {
def insert( id : Int ) {
println("id =" + id)
}
}
class MySQL004{
}
id =10
5 当作富接口使用的特质
- 即该特质中既有抽象方法,又有非抽象方法
6 特质中的具体字段
- 特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。
- 混入该特质的类就具有了该字段,字段不是继承,而是简单的加入类。是自己的字段。
- Java中的接口中的字段只能是公开静态常量。
trait FieldDemo{
var name = "zhangfei"
var age:Int
def testName
def printlnName={
println(name)
}
}
class FieldImplDemo extends FieldDemo{
override var age: Int = _
override def testName: Unit = ???
}
7 特质中的抽象字段
- 特质中未被初始化的字段在具体的子类中必须被重写。
8 特质构造顺序
- 特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成。具体实现请参考“叠加在一起的特质”
步骤总结:
1)、调用当前类的超类构造器
2)、第一个特质的父特质构造器
3)、第一个特质构造器
4)、第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
5)、第二个特质构造器
6)、当前类构造器
9 初始化特质中的字段
- 在某些情况下, Scala 特质和 Java 抽象类是非常类似的,所以也有可能会在声明属性时,出现属性值发生改变的情况,问题请参考“构造顺序和提起定义”
10 扩展类的特质
10.1 特质可以继承自类,以用来拓展该类的一些功能
trait LoggedException extends Exception{
def log(): Unit ={
println(getMessage()) // 方法来自于 Exception 类
}
}
10.2 所有混入该特质的类,会自动成为那个特质所继承的超类的子类
class UnhappyException extends LoggedException{
override def getMessage = "错误消息! "// 已经是 Exception 的子类了,所以可以重写方法
}
10.3 如果混入该特质的类,已经继承了另一个类,不就矛盾了? 注意,只要继承的那个类是特质超类的子类即可。
object Scala05_Trait {
def main(args: Array[String]): Unit = {
val log = new UnhappyException
log.log()
}
}
trait LoggedException extends Exception{
def log(): Unit ={
println(getMessage()) // 方法来自于 Exception 类
}
}
class MySelfClass extends IndexOutOfBoundsException{
}
//保证MySelfClass和LoggedException最后有相同的父类
class UnhappyException extends MySelfClass with LoggedException{
override def getMessage = "错误消息! "// 已经是 Exception 的子类了,所以可以重写方法
}
11 自身类型
- 主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型。
//自身类型特质
trait Logger{
this: Exception => // 明确告诉编译器,我就是 Exception
def log(): Unit ={
println(getMessage) // 既然我就是 Exception, 那么就可以调用其中的方法
}
}
class MySQL extends Exception { // 此处必须继承 Exception 类,否则无法混入 logger特质
}
object Scala05_Trait {
def main(args: Array[String]): Unit = {
// val log = new UnhappyException
// log.log()
val mysql = new MySQL with Logger
mysql.log()
}
}
null