开发者学堂课程【Scala 核心编程-基础:在特质中重写抽象方法】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/609/detail/8997
在特质中重写抽象方法
内容介绍:
一、在特质中重写抽象方法特例
二、在特质中重写抽象方法
一、在特质中重写抽象方法特例
1、提出问题,看段代码
trait Operate5 {
def insert(id : Int) 抽象方法
}
trait File5 extends Operate5 { 用 File5 继承 Operate5
def insert( id : Int ): Unit = { 实现方法
printn("将数据保存到文件中.")
super.insert(id) 调用,把父类的方法实现了,又调用父特征的方法,跟特质混入有关系
}
}
2、运行代码,并小结问题
新建一个包,命名为 mixindemo02。选择 object。
代码的语法没有问题,但是这个代码一定会错,因为调用会调用 inser 父类的 super,而 insert 里面是没有实现的,这个错误会在运行时报出来,错误是 Error:(15, 11) method insert in trait Operate5 is accessed from super. It may not be abstract unless it is overridden by a member declared ‘abstract' and ’override' super.insert(id)不是抽象的,加上关键字,‘abstract' and ’override' ,在 特质里面有一个方法,重写了父类方法,但是仍然是抽象方法,针对这个问题的解决方法是加上 abstract override def insert(id:int):unit={,再次运行代码就可以了。
//说明
//1. 如果我们在子特质中重写/实现了一个父特质的抽象方法,但是同时调用super
//2.这时我们的方法不是完全实现,因此需要声明为 abstract override,调用者注意,还需要实现这个方法,不然仍然不能用
//3.这时 super . insert(id) 的调用跟动态混入的顺序有密切的关系,动态 super 它不一定指的是父类,如果 super 是左边混入的 insert,代码就可以正常运行,因为有时看源码,写法都是这样的,
abstradt override def insert( id : Int ): Unit = {
println("格数据保存到文件中..")
super . insert(id)
}
}
代码错误的原因是,没有完全的实现 insert,同时还没有声明 abstract overrid。
3、解决问题
方式1:去掉 super()...
方式2:调用父特质的抽象方法,那么在实际使用时,没有方法的具体实现,无法编译通过,为了避免这种情况的发生。可重写抽象方法,就是加入 abstract overrid,这样在使用时,就必须考虑动态混入的顺序问题。
package com. atguigu. chapter08. mixin
object MixInDemo02 {
def main(args: Array[String]): Unit = {
//println("xxx")
val mySQL5 = new MySQL5 with DB5 with File5
//1. 将数据保存到文件中
//2. 将数据保存到数据库中
mySQL5. insert(666)
}
}
trait Operate5 {
def insert(id : Int)
}
trait File5 extends Operate5 { 调用 File5,找到 File5
//说明
//1.如果我们在子特质中重写/实现了一个父 特质的抽象方法,但是同时调用 super
//2.这时我们的方法不是完全实现,因此需要声明为 abstract override
//3.这时 super. insert(id)的调用就和动态混入顺序有密切关系
abstract override def insert( id : Int ): Unit = { 构建这个地方跟方法没有关系,因此不受任何影响,正常是不可以调用,但是有abstract,这就是它重写抽象的特点,正常重写抽象不可以调用,但是它可以调用
println("将数据保存到文件中..") 没有再调用 super,终止后代码得到顺利的执行
super . insert(id)
}
}
trait DB5 extends Operate5 {//我们继承 Operate5,并实现了Operate 的 insert
def insert( id: Int ): Unit= {
printIn("将数据保存到数据库中..")
}
}
class MySQL5 []
val mysql5 = new MySQL5 with DB5 with File5
执行后,代码没有任何问题,这时必须考虑动态混入顺序,假如把代码进行颠倒,从编译来看,它并没有报错。
val mySQL5_ = new MySQL5 with File5 with DB5
mySQL5. insert(66)
这样执行会报错,因为 DB5,构建仍然是正常执行,不混入 DB5,只混入 File5,就出现报错。
//下面的混入方式错误
//val mySQL5_ = new MySQL5 with File5
//mySQL5. insert(66)
只混入DB5是可以的,如果只混入 File5,那么就会进行报错,因为编译层面就没有进行控制,编译如果通过,File5到super 没有地方跑,因为左边没有东西。
二、在特质中重写抽象方法
1、理解 abstract override 的小技巧分享:
可以理解,当我们给某个方法增加了 abstract override 后,就是明确的告诉编译器,该方法确实是重写了父特质的抽象方法,但是重写后,该方法只仍然是一个抽象方法,因为没有完全的实现,需要其它特质继续实现,通过混入顺序。混入是产生新的对象,它的类的关系是重新构建的,它仍然会进行 super 的调用。
2、重写抽象方法时需要考虑混入特质的顺序问题和完整性问题看4个案例,并判断结果。
var mysq|2 = new MySQL5 with DB5 //ok 因为 DB5就是简单的实现
mysq|2.insert(100)
var mysql3 = new MySQL5 with File5 //error 因为 super 已经找不到东西了
mysql2.insert(100)
var mysq|4 = new MySQL5 with File5 with DB5// error
mysq|4.insert(100)
首先它找到 DB5,没有执行,断掉了,这里只输出一句话,将数据保存到数据库中,执行后报错,它在混入时是继承的重组。
var mysql4 = new MySQL5 with DB5 with File5//ok
mysql4.insert(100)