前言
在scala中的抽象类能有的成员, 那么特质(trait
)都可有
- 属性
- 方法
- 抽象属性
- 抽象方法
- 构造器(主/辅)
与抽象类的区别
抽象类只能单继承,trait
可以多混入!
class A extends t1 with t2 with t3...
叠加冲突
由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种
第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。
第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。
这个解决方案是给TraintA和TraitB找一个共同的父TraitC,在C里面也声明相同的冲突方法。最后会调用的是特质叠加的最后一个trait里面的冲突方法!
所谓的特质叠加是指将多个trait全部叠加到一起!
下面一个示例来说明!
trait Ball {
def describe(): String = {
"ball"
}
}
trait Color extends Ball {
override def describe(): String = {
"blue-" + super.describe()
}
}
trait Category extends Ball {
override def describe(): String = {
"foot-" + super.describe()
}
}
class MyBall extends Category with Color {
override def describe(): String = {
"my ball is a " + super.describe()
}
}
object TestTrait {
def main(args: Array[String]): Unit = {
println(new MyBall().describe())
}
}
运行结果!
一般来说调用顺序是 class MyBall extends Category with Color
从右至左,所以color先被调用!而在color中使用了super来调用方法!注意这里调用的会是兄弟trait里面的describe方法!而不是父类的!一定要注意!!!!
如果想让color使用super调用父类的describe方法需要做下面的改写!
class MyBall extends Category with Color {
override def describe(): String = {
"my ball is a " + super[Ball].describe()
}
}
总结
冲突的方法最终使用的是最后叠加的那个!!!
- 初始化的是, 一个
trait
最多初始化一次 - 初始化的时候是从父开始, 从左
super.describe()
不是真正的找父类, 而是按照叠加的顺序向前找super[Ball].describe()
明确指定这个super
应该是哪个类
特质继承类注意事项
class M
class A{
def foo() = {
println("A... foo")
}
}
trait B extends A{
def eat() = {
println("B ... eat")
foo()
}
}
// extends 要么是A要么A的子类
class C extends A with B with M
这里会报错!因为类只能单继承!M是一个类,与A没有关系,C先继承了A那么如果混入M则M必须是A的子类!所以
将M extends A之后则编译才可以通过!
总结
混入时必须注意类之间的关系!看extends后面的类!他来限制后面with中的class!
动态叠加
可以在new对象时with一些trait
例如
new C() with trait A with trait B.......
总结
trait的主构造器为无参构造器!
缺省的(没有加val或者var)变量作为参数时,这个参数看作一个不可变的局部变量!