十、特质trait
类似于Java中的接口
语法
-
特质关键词
trait
-
特质可以同时用于抽象方法和具体方法
-
无父类 实现特质
extends trait1 with trait2 with trait3 ...
-
有父类 实现特质
extends 父类 with 特质1 with 特质2 ...
当做接口的特质
- Scala可以完全像Java接口一样工作, 你不需要将抽象方法声明为 abstract, 特质中未被实现的方法默认就是抽象方法;
- 类可以通过 extends 关键字继承特质, 如果需要的特质不止一个, 通过 with 关键字添加额外特质
- 重写特质的抽象方法时, 不需要 override 关键字
- 所有 Java 接口都可以当做 Scala 特质使用
trait Animals extends Serializable with Cloneable
package traits
// 注意: java的接口可以当做scala的trait使用
// trait Animals extends Serializable with Cloneable
// 等价于: public interface Animals {}
trait Animals {
// 只有方法的声明,没有实现
// 等价于 void eat();
def eat(): Unit
}
// 如果当前类Dog没有父类 extends 特质
class Dog extends Animals {
// 等价于 实现接口中的 public void eat()
override def eat(): Unit = println("dog eat shit")
}
object AnimalsDemo {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.eat()
}
}
带有具体实现的特质
trait Animals {
def eat(food: String): String
def sleep():Unit = println("animals sleep~~~") // 特质带有方法实现的方法
}
注意:让特质混有具体行为有一个弊端. 当特质改变时, 所有混入该特质的类都必须重新编译
带有特质的对象 动态混入(mixin) [ 重点]
在构造单个对象时, 你可以为它添加特质
- 特质可以将对象原本没有的方法与字段加入对象中, 功能扩展
- 无需修改类声明定义,扩展类的功能,灵活简便
- 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
package traits.test1.mixin
trait Animals {
def eat(): Unit
}
trait Sheep extends Animals {
// 实现特质声明的方法
override def eat() = {
println("吃草")
}
def run = println("在跑...")
def sleep
}
class Person extends Animals {
override def eat(): Unit = {
println("吃食物")
}
}
object Person {
def main(args: Array[String]): Unit = {
val person = new Person with Sheep {
override def sleep: Unit = println("睡觉觉!!!")
} // 动态混入
person.eat // Person 和 Sheep都有eat方法,调用时从右往左调用 输出 吃草
person.run // Person 中没有run方法,Sheep特质将自己的方法混入Person
person.sleep // 睡觉觉!!!
}
}
this别名
看scala的源码的话很发现很多源码开头都有一句:self =>
这句相当于给this
起了一个别名为self
。self
不是关键字,可以用除了this
外的任何名字命名(除关键字)。就下面的代码,在Student内部,可以用this
指代当前对象,也可以用self
指代,两者是等价的。
class Student {
self => // this起别名
val x = 2
def foo = self.x + this.x
}
object Student {
def main(args: Array[String]): Unit = {
val s1 = new Student
println(s1.foo) //4
}
}
它的一个场景是用在有内部类的情况下:
class Outer {
outer =>
val v1 = "here"
class Inner {
inner =>
val v1 = "-------------"
println(outer.v1) // 用outer表示外部类,相当于Outer.this
println(inner.v1)
}
}
self-type
强制混入
自类型用于声明一个特质必须混入其他特质,尽管该特质没有直接扩展其他特质。 这使得所依赖的成员可以在没有导入的情况下使用。
自类型是一种细化 this
或 this
别名之类型的方法。 语法看起来像普通函数语法,但是意义完全不一样。
要在特质中使用自类型,写一个标识符,跟上要混入的另一个特质,以及 =>
(例如 someIdentifier: SomeOtherTrait =>
)
package traits
trait A {
val name: String
}
trait B {
this: A => // this:混入类型 => [self type] 在特质中混入特质,所依赖的成员可以再没有导入的情况下使用
def sayHi = s"Hello:$name"
}
class C(val name: String) extends B with A {
}
object C {
def main(args: Array[String]): Unit = {
val c = new C("zs")
println(c.sayHi)
}
}