特质
特质的混入用法
特质的叠加
特质和抽象类的区别
特质自身类型
特质
- Scala 语言中,采用特质 trait(特征)来代替接口的概念,也就是说,多个类具有相同 的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字 trait 声明。
- Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可 以混入(mixin)多个特质。
- 这种感觉类似于 Java 中的抽象类。 Scala 引入 trait 特征,第一可以替代 Java 的接口,第二个也是对单继承机制的一种
trait 特质名 {
trait 主体
}
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素, 所以在使用时,也采用了 extends 关键字,如果有多个特质或存在父类,那么需要采用 with 关键字连接。
没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3 …
有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3
- 类和特质的关系:使用继承的关系。
- 当一个类去继承特质时,第一个连接词是 extends,后面是 with。
- 如果一个类在同时继承特质和父类时,应当把父类写在 extends 后。
当父类和特质定义相同属性发生冲突时出现如下报错提示 则需要重写父类和特质的冲突属性
object traits {
def main(args: Array[String]): Unit = {
//测试
val st = new z
st.hello()
st.Run()
st.study()
}
}
//定义父类
class S{
val name : String = "s"
var age : Int = 20
def hello(): Unit ={
println("hello"+name)
}
}
//定义特质
trait Young{
//声明抽象和非抽象属性
var age : Int
val name : String = "young"
//声明抽象和非抽象方法
def play(): Unit ={
println("playing games")
}
def Run(): Unit
}
//定义子类
class z extends S with Young {
//重写冲突属性
override val name : String = "zz"
//重写特质抽象方法
override def Run(): Unit = {
println(s"${name}跑步")
}
//重写父类方法
override def hello(): Unit = {
super.hello()
println("你好")
}
//定义子类方法
def study(): Unit ={
println(" 学习")
}
}
特质的混入用法
混入是可灵活的扩展类的功能
动态混入:创建对象时混入 trait,而无需使类混入该 trait
如果混入的 trait 中有未实现的方法,则需要实现
object traits {
def main(args: Array[String]): Unit = {
//测试
val st = new Ta
st.study()
st.increase()
st.play()
st.increase()
println("------------------------------------------------")
//动态混入
val st2 = new Ta with Take {
//重写特质3抽象类
override def Singing(): Unit = println("唱歌")
override def dancing(): Unit = println("跳舞")
}
st2.hello()
st2.play()
st2.Singing()
st2.dancing()
st2.increase()
}
}
//定义父类
class S{
val name : String = "s"
var age : Int = 20
def hello(): Unit ={
println("hello"+name)
}
}
//定义特质1
trait Young{
//声明抽象和非抽象属性
var age : Int
val name : String = "young"
//声明抽象和非抽象方法
def play(): Unit ={
println("playing games")
}
def Run(): Unit
}
//定义特质2
trait Knowledge{
var amount: Int = 0
def increase(): Unit
}
//定义特质用于动态混入
trait Take{
def Singing(): Unit
def dancing(): Unit
}
//定义混入特质类
class Ta extends S with Young with Knowledge {
//重写冲突属性
override val name: String = "zz"
//重写S父类方法
override def hello(): Unit = {
super.hello()
println("你好")
}
//定义子类方法
def study(): Unit = {
println(" 学习")
}
//重写Young特质抽象方法
override def Run(): Unit = {
println(s"${name}跑步")
}
//重写Knowledge特质抽象方法
override def increase(): Unit = {
amount += 1
println(name+" :"+amount)
}
}
特质的叠加
所谓的特质叠加,就是将混入的多个 trait 中的冲突方法叠加起来。
由于一个类可以混入(mixin)多个 trait,且 trait 中可以有具体的属性和方法,若混入 的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。 冲突分为以下两种:
- 第一种,一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且 两个 trait 之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。
object traits {
def main(args: Array[String]): Unit = {
//测试
val stt1 = new studentz
stt1.increase()
}
}
//定义特质1
trait Knowledge{
var amount: Int = 0
def increase(): Unit = {
println("---->")
}
}
//定义特质2
trait Take{
def Singing(): Unit
def dancing(): Unit
def increase(): Unit = {
println("<----")
}
}
//定义父类
class S{
val name : String = "s"
var age : Int = 20
def hello(): Unit ={
println("hello"+name)
}
def increase(): Unit = {
println("<------>")
}
}
//定义叠加特质类
class studentz extends S with Knowledge with Take{
//重写特质抽象方法
override def Singing(): Unit = println("唱歌")
override def dancing(): Unit = println("跳舞")
override def increase(): Unit ={
super.increase()
}
}
- 第二种,一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且 两个 trait 继承自相同的 trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala 采用了特质叠加的策略。
object traits {
def main(args: Array[String]): Unit = {
//测试
val my = new My
println(my.describe())
}
}
//定义super特质
trait A{
def describe(): String = "AAAA"
}
//定义super特质的子特质1
trait B extends A{
var b : String = "b"
//重写特质super抽象方法
override def describe(): String = super.describe()+b
}
//定义super特质的子特质2
trait C extends A {
var c : String = "c"
//重写特质super抽象方法
override def describe(): String = super.describe()+c
}
//定义叠加特质类
class My extends B with C {
//重写特质抽象方法
override def describe(): String = super.describe()
}
class My extends B with C
叠加顺序 C+B+A
- 如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如:
特质和抽象类的区别
- 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
- 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数, 而特质不行(有无参构造)。
特质自身类型
_: 类型名称 =>
使用this.属性 或 类名.属性 可直接访问
自身类型可实现依赖注入的功能。
object traits {
def main(args: Array[String]): Unit = {
//测试
val user = new RegisterUser("name","password")
user.insert()
}
}
//定义用户类
class User(val name : String,val password : String)//没主体可省略花括号不写
trait UserDao{
//定义自身类型
_: User =>
//向数据库插入数据
def insert(): Unit ={
println(s"insert into db :${this.name}")
}
}
//定义子类注册用户类 继承父类属性 混入特质
class RegisterUser(name : String , password : String) extends User(name,password) with UserDao