特质(Trait)
Scala中没有接口(interface)的概念
特质用于在类之间共享程序接口和字段,类似于Java接口
特质是字段和方法的集合,可以提供字段和方法实现
类和单例对象都可以扩展特质(extends)
特质不能被实例化,因此没有构造参数,类似于Java接口
特质使用“trait”关键字定义
实现特质中的方法使用“override”
使用特质
import scala.collection.mutable.ArrayBuffer
trait Pet {
val name: String
def cry():Unit
}
class Dog(val name: String) extends Pet{
override def cry()=println("wow ...")
}
val dog = new Dog("Harry")
val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.foreach(pet => {println(pet.name);pet.cry()}) // Prints Harry wow ...
混入特质(mixin)
当某个特质被用于组合类时,被称为混入
一个类只能有一个父类但是可以有多个混入(分别使用关键字extends和with)
abstract class A {
val message: String
}
class B extends A {
val message = "I'm an instance of class B"
}
trait C extends A {
def loudMessage = message.toUpperCase()
}
//构造顺序由左往右,如果前面已经构造了某个父类,后面子类的该父类不会重复构造
class D extends B with C
val d = new D
println(d.message) // I'm an instance of class B
println(d.loudMessage) // I'M AN INSTANCE OF CLASS B
动态混入特质
class Drawing {
//this:Type=> 自身类型,表示该类实例化时必须混入相应特质或子特质,self是this的别名。
self: Shape =>
def start(): Unit = draw()
}
trait Shape {
def draw(): Unit
}
trait Square extends Shape {
def draw(): Unit = println("draw a square")
}
trait Triangle extends Shape {
def draw(): Unit = println("draw a triangle")
}
//动态混入
(new Drawing() with Square).start()
(new Drawing() with Triangle).start()
特质与抽象类的选择
优先使用特质
- 抽象类只能继承一次
- 特质可以混入多个
需要使用带参构造方法时,使用抽象类
与Java互操作性
- 抽象类与Java完全可互操作
- 特质只有在不包含任何实现代码时才可互操作
示例
package packagetwo
abstract class Car {
def brand:String
def engine:String
def didi():String={
"汽车鸣笛滴滴滴"
}
}
class BMW extends Car{
override def brand: String = {
"德国宝马"
}
override def engine: String = {
"16缸涡轮增压"
}
}
class Dazhong extends Car{
override def brand: String ={
"上海大众"
}
override def engine: String = {
"18缸火箭喷气"
}
}
trait Type1{
def fly:Unit={
println("可以飞")
}
}
trait Type2{
def downsea():Unit={
println("可以下海")
}
}
object DemoCar{
def main(args: Array[String]): Unit = {
val bmw=new BMW
println(bmw.brand)
println(bmw.engine)
println(bmw.didi())
val dazhong:Dazhong with Type1 with Type2=new Dazhong with Type1 with Type2
dazhong.fly
dazhong.downsea()
println(dazhong.brand)
println(dazhong.engine)
}
}
结果展示:
型变
协变
class Foo[+T] // 协变类
对于两种类型A和B,如果A是B的子类型,那么Foo[A]就是Foo[B]的子类型
逆变
class Bar[-T] // 逆变类
对于两种类型A和B,如果A是B的子类型,那么Bar[B]就是Bra[A]的子类型
不变
class Baz[T] // 不变类
默认情况下,Scala中的泛型类是不变的
示例
object test {
//协变点(covariant position)、逆变点(contravariant position)和不变(invariant)
class Animal{
def eat():Unit={
println("动物要吃食物")
}
}
class Cat extends Animal{
override def eat(): Unit = println("猫吃鱼")
}
class Tiger extends Cat{
override def eat(): Unit = println("老虎吃肉")
}
class Invariant[T]{//不变
}
class Covariant[+T]{//协变
}
class Inversion[-T]{//逆变
}
def main(args: Array[String]): Unit = {
var cat=new Cat
var tiger=new Tiger
val cat2:Cat=tiger
val bubian:Invariant[Cat]=new Invariant[Cat]
val bubianTiger:Invariant[Tiger]=new Invariant[Tiger]
/*报错(在泛型里不变的情况下Invariant[T],Invariant[Cat]与Invariant[Tiger]没有任何变系):
val bubian2:Invariant[Cat]=bubianTiger*/
val xieCat:Covariant[Cat]=new Covariant[Cat]
val xietiger:Covariant[Tiger]=new Covariant[Tiger]
val xieCat2:Covariant[Cat]=xietiger
val nicat:Inversion[Cat]=new Inversion[Cat]
val nitiger:Inversion[Tiger]=new Inversion[Tiger]
val nitiger2:Inversion[Cat]=nicat
}
}