Scala之特质特质Trait
一、概述
Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特征(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。
Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。
Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。
二、特质声明
1)基本语法
trait 特质名 {
trait体
}
2)案例实操
trait PersonTrait {
// 声明属性
var name:String = _
// 声明方法
def eat():Unit={
}
// 抽象属性
var age:Int
// 抽象方法
def say():Unit
}
三、特质基本语法
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接。
1)基本语法:
没有父类:class 类名 extends 特质1 with 特质2 with 特质3 …
有父类:class 类名 extends 父类 with 特质1 with 特质2 with 特质3…
2)说明
(1)类和特质的关系:使用继承的关系。
(2)当一个类去继承特质时,第一个连接词是extends,后面是with。
(3)如果一个类在继承特质和父类时,应当把父类写在extends后。
3)案例实操
package com.scala.charpter06
object Triat_test {
class Animal
trait Logger{
//具体属性
val name = "zahngsan"
//抽象属性
val age:Int
//具体方法
def m1(x:Int,y:Int) = x+y
//抽象方法
def printMag:Unit
}
trait Logger2
//子类不需要继承父class时,extends用于实现第一个trait,后续实现其他trait使用with关键字
class WarnLogger extends Logger with Logger2{
override val age: Int =20
override def printMag: Unit = println("WarnLogger.....")
}
子类需要继承父class时,extends用于继承trait,后续实现其他trait使用with关键字
class ErrorLogger extends Animal with Logger with Logger2{
override val age: Int = 400
override def printMag: Unit = println("ErrorLog............")
}
/**
* scala中是单继承实现trait特质
*
* 特质的语法:trait 特质名{...}
*
* scala中子类实现特质分为两种情况:
* 1.子类不需要继承父class时,extends用于实现第一个trait,后续实现其他trait使用with关键字
* 2.子类需要继承父class时,extends用于继承trait,后续实现其他trait使用with关键字
*
* 特质中既可以定义抽象方法也可以定义具体方法,既可以定义抽象属性也可以定义具体属性
*
* @param args
*/
def main(args: Array[String]): Unit = {
var logger = new ErrorLogger
println(logger.name)
println(logger.age)
logger.printMag
println(logger.m1(10, 20))
}
}
四、特质叠加
由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:
- 第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。
- 第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。
所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来,案例如下,
package com.scala.charpter06
object TraitMixture {
trait Logger{
val name = "zhangsan"
def m1(x:Int,y:Int)=x+y
}
class WarnLogger extends Logger
class ErrorLogger
/**
* 特质混入:只让某个对象拥有特质的属性和方法
*
* 语法:new 类名(..) with 特质名
*
* @param args
*/
def main(args: Array[String]): Unit = {
val logger = new WarnLogger
println(logger.name)
println(logger.m1(10, 20))
val logger2 = new ErrorLogger with Logger
println(logger2.name)
println(logger2.m1(20, 30))
}
}
五、特质叠加执行顺序
如果子类继承多个父trait之间有关系[继承同一个父trait],在子类中通过super调用同名方法的时候,方法的执行顺序是按照继承顺序从右向左开始执行,最后执行多个父trait的父trait
package com.scala.charpter06
object TraitMulit {
trait Logger{
def log(msg:String)=println("Logger..........")
}
trait Logger1 extends Logger {
override def log(msg:String)={
println("Logger1..........")
super.log("xxx")
}
}
trait Logger2 extends Logger {
override def log(msg:String)= {
println("Logger2..........")
super.log("xxx")
}
}
trait Logger3 extends Logger {
override def log(msg:String)={
println("Logger3..........")
super.log("xxx")
}
}
class ErrorLogger extends Logger1 with Logger2 with Logger3{
override def log(msg:String)= {
println("Errorlogger..........")
//默认调用最后一个父trait的同名方法
super.log("xxx")
//指定调用哪个父trait的方法
super[Logger2].log("yy")
}
}
/**
*子类可以实现多个特质:
* 如果这多个特质中都有同名方法,并且参数列表也一样,那么子类继承之后会出现冲突
*
*解决方法:子类重写同名方法
*
*如果子类继承多个父trait之间有关系[继承同一个父trait],在子类中通过super调用同名方法的时候,方法的执行顺序
是按照继承顺序从右向左开始执行,最后执行多个父trait的父trait
*
* @param args
*/
def main(args: Array[String]): Unit = {
val logger = new ErrorLogger
logger.log("hello")
}
}
六、特质自身类型
1)说明
自身类型可实现依赖注入的功能。
2)案例实操
package com.scala.charpter06
import java.io.{FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutputStream}
object TraitSelfType {
class Person(val name:String, val age:Int) extends ReadAndWrite with Serializable
trait ReadAndWrite{
//指定必须继承的类
_:Serializable =>
//从磁盘文件读取对象
def read() ={
val ois: ObjectInputStream = new ObjectInputStream(new FileInputStream("d:/person.txt"))
val obj: AnyRef = ois.readObject()
ois.close()
obj
}
//将对象写入磁盘
def write()={
val oos: ObjectOutputStream = new ObjectOutputStream(new FileOutputStream("d:/person.txt"))
oos.writeObject(this)
oos.close()
}
}
/**
* 特质自身类型:
* 指定子类在继承trait的时候必须先继承或者实现某一个指定的trait或者class
*
* 语法: this:类型=>
*
* @param args
*/
def main(args: Array[String]): Unit = {
val person: Person = new Person("lisi",30)
person.write()
val person2 = new Person("zhangsan",20)
val obj = person2.read()
val person3: Person = obj.asInstanceOf[Person]
println(person3.name)
println(person3.age)
}
}