Scala语言中,采用trait(特质,特征)来代替接口的概念,也就是说,多个类具有相同的特征(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明
trait 特质名 {
trait体
}
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接
- 没有父类
class 类名 extends 特质1 with 特质2 with 特质3 ..- 有父类
class 类名 extends 父类 with 特质1 with 特质2 with 特质3
object Trait02 {
def main(args: Array[String]): Unit = {
//使用
val c = new C
val e = new E
//调用了C类实现的Trait1的geConnenct
c.getConnect("root","12345")
e.getConnect("scott","12345")
}
}
//这是一个Trait1
trait Trait1 {
//声明方法,抽象的.
def getConnect(user: String, pwd: String): Unit
}
class A {}
class B extends A {}
class C extends A with Trait1 {
override def getConnect(user: String, pwd: String): Unit = {
println("连接到mysql数据库")
}
}
class D {}
class E extends D with Trait1 {
def getConnect(user: String, pwd: String): Unit = {
println("e连接oracle")
}
}
class F extends D {}
动态混入
1)除了可以在类声明时继承特质以外,还可以在构建对象时混入特质,扩展目标类的功能【反编译看动态混入本质】
2)此种方式也可以应用于对抽象类功能进行扩展
3)动态混入是Scala特有的方式(java没有动态混入),可在不修改类声明/定义的情况下,扩展类的功能,非常的灵活,耦合性低 。
4)动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能。[如何理解]
5)抽象类中有 抽象的方法,如何动态混入特质->可以,在创建实例时,实现抽象方法即可
object MixInDemo {
def main(args: Array[String]): Unit = {
//创建OracleDB 实例,同时动态混入Operate3特质
//就可以使用特质的方法,理解解耦接入.
val oracleDB = new OracleDB with Operate3 {
override def insert2(): Unit = {
println("insert2")
}
}
oracleDB.insert(100)//
oracleDB.insert2()
new MySQL3 with Operate3 {
override def insert2(): Unit = {
}
}
//如果我们要去实例化一个abstract 类,也可以,但是需要时候用匿名子类来构建
//语句
val mySQL = new MySQL3 {
override def sayHi: Unit = {
}
}
}
}
//特质
trait Operate3 {
def insert(id: Int): Unit = {
println("插入数据 = " + id)
}
def insert2()
}
//普通类
class OracleDB {
}
//抽象类
abstract class MySQL3 {
def sayHi
}
叠加特质
构建对象的同时如果混入多个特质,称之为叠加特质,那么特质声明顺序从左到右,方法执行顺序从右到左。
1)特质声明顺序从左到右。
2)Scala在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行
3)Scala中特质中如果调用super,并不是表示调用父特质的方法,而是向前面(左边)继续查找特质,如果找不到,才会去父特质查找
如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛型必须是该特质的直接超类类
//看看混入多个特质的特点(叠加特质)
object AddTraits {
def main(args: Array[String]): Unit = {
//说明
//1. 创建 MySQL4实例时,动态的混入 DB4 和 File4
//研究第一个问题,当我们创建一个动态混入对象时,其顺序是怎样的
//总结一句话
//Scala在叠加特质的时候,会首先从后面的特质开始执行(即从左到右)
//1.Operate4...
//2.Data4
//3.DB4
//4.File4
val mysql = new MySQL4 with DB4 with File4
println(mysql)
//研究第2个问题,当我们执行一个动态混入对象的方法,其执行顺序是怎样的
//顺序是,(1)从右到左开始执行 , (2)当执行到super时,是指的左边的特质 (3) 如果左边没有特质了,则super就是父特质
//1. 向文件"
//2. 向数据库
//3. 插入数据 100
mysql.insert(100)
println("===================================================")
//练习题
val mySQL4 = new MySQL4 with File4 with DB4
mySQL4.insert(999)
//构建顺序
//1.Operate4...
//2.Data4
//3.File4
//4.DB4
//执行顺序
//1. 向数据库
//2. 向文件
//3. 插入数据 = 999
}
}
trait Operate4 { //特点
println("Operate4...")
def insert(id: Int) //抽象方法
}
trait Data4 extends Operate4 { //特质,继承了Operate4
println("Data4")
override def insert(id: Int): Unit = { //实现/重写 Operate4 的insert
println("插入数据 = " + id)
}
}
trait DB4 extends Data4 { //特质,继承 Data4
println("DB4")
override def insert(id: Int): Unit = { // 重写 Data4 的insert
println("向数据库")
super.insert(id)
}
}
trait File4 extends Data4 { //特质,继承 Data4
println("File4")
override def insert(id: Int): Unit = { // 重写 Data4 的insert
println("向文件")
//super.insert(id) //调用了insert方法(难点),这里super在动态混入时,不一定是父类
//如果我们希望直接调用Data4的insert方法,可以指定,如下
//说明:super[?] ?的类型,必须是当前的特质的直接父特质(超类)
super[Data4].insert(id)
}
}
class MySQL4 {} //普通类
声明式混入和动态混入的区别是:
声明式混入,是先执行父类的构造器,以及特质的构造器,最后才是自己的构造器
动态混入,是先执行父类的构造器,然后自己的构造器,然后特质的构造器
object MixInSeq {
def main(args: Array[String]): Unit = {
//这时FF是这样 形式 class FF extends EE with CC with DD
/*
调用当前类的超类构造器
第一个特质的父特质构造器
第一个特质构造器
第二个特质构造器的父特质构造器, 如果已经执行过,�就不再执行
第二个特质构造器
.......重复4,5的步骤(如果有第3个,第4个特质)
当前类构造器 [案例演示]
*/
//1. E...
//2. A...
//3. B....
//4. C....
//5. D....
//6. F....
val ff1 = new FF()
println(ff1)
//这时我们是动态混入
/*
先创建 new KK 对象,然后再混入其它特质
调用当前类的超类构造器
当前类构造器
第一个特质构造器的父特质构造器
第一个特质构造器.
第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
第二个特质构造器
.......重复5,6的步骤(如果有第3个,第4个特质)
当前类构造器 [案例演示]
*/
//1. E...
//2. K....
//3. A...
//4. B
//5. C
//6. D
println("=======================")
val ff2 = new KK with CC with DD
println(ff2)
}
}
trait AA {
println("A...")
}
trait BB extends AA {
println("B....")
}
trait CC extends BB {
println("C....")
}
trait DD extends BB {
println("D....")
}
class EE { //普通类
println("E...")
}
class FF extends EE with CC with DD { //先继承了EE类,然后再继承CC 和DD
println("F....")
}
class KK extends EE { //KK直接继承了普通类EE
println("K....")
}
拓展类的特质
- 特质可以继承类,以用来拓展该特质的一些功能
- 所有混入该特质的类,会自动成为那个特质所继承的超类的子类
- 如果混入该特质的类,已经继承了另一个类(A 类),则要求 A 类是特质超类的子类,否则就会出现了多继承现象,发生错误
object ExtendTraitDemo01 {
def main(args: Array[String]): Unit = {
println("haha~~")
}
}
//说明
//1. LoggedException 继承了 Exception
//2. LoggedException 特质就可以 Exception 功能
trait LoggedException extends Exception {
def log(): Unit = {
println(getMessage()) // 方法来自于Exception类
}
}
//因为 UnhappyException 继承了 LoggedException
//而 LoggedException 继承了 Exception
//UnhappyException 就成为 Exception子类
class UnhappyException extends LoggedException{
// 已经是Exception的子类了,所以可以重写方法
override def getMessage = "错误消息!"
}
// 如果混入该特质的类,已经继承了另一个类(A类),则要求A类是特质超类的子类,
// 否则就会出现了多继承现象,发生错误。
class UnhappyException2 extends IndexOutOfBoundsException with LoggedException{
// 已经是Exception的子类了,所以可以重写方法
override def getMessage = "错误消息!"
}
class CCC {}
//错误的原因是 CCC 不是 Exception子类
//class UnhappyException3 extends CCC with LoggedException{
// // 已经是Exception的子类了,所以可以重写方法
// override def getMessage = "错误消息!"
//}
自身类型
主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型
object ExtendTraitDemo01 {
def main(args: Array[String]): Unit = {
println("haha~~")
}
}
//说明
//1. LoggedException 继承了 Exception
//2. LoggedException 特质就可以 Exception 功能
trait LoggedException extends Exception {
def log(): Unit = {
println(getMessage()) // 方法来自于Exception类
}
}
//因为 UnhappyException 继承了 LoggedException
//而 LoggedException 继承了 Exception
//UnhappyException 就成为 Exception子类
class UnhappyException extends LoggedException{
// 已经是Exception的子类了,所以可以重写方法
override def getMessage = "错误消息!"
}
// 如果混入该特质的类,已经继承了另一个类(A类),则要求A类是特质超类的子类,
// 否则就会出现了多继承现象,发生错误。
class UnhappyException2 extends IndexOutOfBoundsException with LoggedException{
// 已经是Exception的子类了,所以可以重写方法
override def getMessage = "错误消息!"
}
class CCC {}
//错误的原因是 CCC 不是 Exception子类
//class UnhappyException3 extends CCC with LoggedException{
// // 已经是Exception的子类了,所以可以重写方法
// override def getMessage = "错误消息!"
//}
网友评论