1、trait基礎知識
1-1 將trait作為接口使用
//1、可以將trait作為接口來使用捣染,與Java類似
trait HelloTrait {
//在trait中可以定義抽象方法鼻弧,只要不給出具體實現(xiàn)即可
def sayHello(name:String)
}
trait MakeFriendsTrait{
def makeFriends(p:Person)
}
//類可以使用extends關鍵字繼承trait唠梨,不是implements,
//在scala中無論繼承類還是trait很泊,統(tǒng)一使用extends
//scala不支持對類進行多繼承囱嫩,但支持多重繼承trait嫂侍,繼承第一個用extends儿捧,后邊的都用with
class Person(val name:String) extends HelloTrait with MakeFriendsTrait with Cloneable {
//類繼承trait后,必須實現(xiàn)其中的抽象方法挑宠,實現(xiàn)時不需要使用override關鍵字
def sayHello(name: String): Unit = println("Hello,"+name)
def makeFriends(p: Person): Unit ={
println("Hello,my name is "+name+",your name is "+p.name)
}
}
//運行代碼:
val p1 = new Person("leo")
val p2 = new Person("Jack")
p1.sayHello("Jack")
p1.makeFriends(p2)
運行結果:
Hello,Jack
Hello,my name is leo,your name is Jack
1-2 在trait中定義具體實現(xiàn)方法
trait Logger {
//trait不僅可以定義抽象方法菲盾,也可以定義具體方法
def log(msg:String)=println("logs:" + msg)
}
class Person(val name:String) extends HelloTrait
with MakeFriendsTrait with Logger {
//類繼承trait后,必須實現(xiàn)其中的抽象方法各淀,實現(xiàn)時不需要使用override關鍵字
def sayHello(name: String): Unit = println("Hello,"+name)
>def makeFriends(p: Person): Unit ={
println("Hello,my name is "+name+",your name is "+p.name)
//此處調用trait實現(xiàn)的方法
log("makeFriends method is invoked withparam p.name:"+p.name)
}
}
運行結果:
Hello,my name is leo,your name is Jack
logs:makeFriends method is invoked withparam p.name:Jack
1-3 在trait中定義具體字段
//1懒鉴、可以將trait作為接口來使用,與Java類似
trait HelloTrait {
//trait可以定義具體的field
val count = 2
//在trait中可以定義抽象方法碎浇,只要不給出具體實現(xiàn)即可
def sayHello(name:String)
}
class Person(val name:String) extends HelloTrait {
//類繼承trait后临谱,必須實現(xiàn)其中的抽象方法咆畏,實現(xiàn)時不需要使用override關鍵字
def sayHello(name: String): Unit = println("Hello,"+name+",count="+count)
}
val p1 = new Person("leo")
p1.sayHello("Jack")
運行結果:
Hello,Jack,count=2
1-4 在trait中定義抽象字段
//1、可以將trait作為接口來使用吴裤,與Java類似
trait HelloTrait {
//trait可以定義具體的field
val count = 2
//trait可以定義抽象的field
val msg:String
//trait可以定義具體實現(xiàn)方法
def sayHello(name:String)=println(msg+","+name)
}
class Person(val name:String) extends HelloTrait{
val msg: String = "Hello"
def makeFriends(p: Person): Unit ={
sayHello(p.name)
println("Hello,my name is "+name+",your name is "+p.name)
}
}
val p1 = new Person("leo")
val p2 = new Person("Jack")
p1.makeFriends(p2)
運行結果:
Hello,Jack
Hello,my name is leo,your name is Jack
2旧找、trait高級知識
2-1 為實例對象混入trait
trait Logger {
def log(msg:String){}
}
trait MyLogger extends Logger{
//trait不僅可以定義抽象方法,也可以定義具體方法
override def log(msg:String)=println("logs:" + msg)
}
class Person(val name:String) extends HelloTrait with Logger {
val msg: String = "Hello"
//類繼承trait后麦牺,實現(xiàn)非抽象方法時钮蛛,必須要要使用override關鍵字
override def sayHello(name: String): Unit = {
println("Hello,"+name+",count="+count)
log("sayHello method is invoked")
}
}
val p1 = new Person("leo")
p1.sayHello("Jack")
val p2 = new Person("Jack") with MyLogger
p2.sayHello("leo")
運行結果:
Hello,Jack,count=2
Hello,leo,count=2
logs:sayHello method is invoked
2-2 trait調用鏈
1、scala支持讓類繼承多個trait剖膳,依次調用多個trait的同一個方法魏颓,只需要在多個trait的同一個方法中最后調用super方法即可
2、類中調用多個trait中都有的方法時吱晒,首先會從最右邊的方法開始執(zhí)行甸饱,然后依次往左執(zhí)行,形成一個調用鏈條
3仑濒、這種特性非常強大叹话,其實就相當于設計模式中的責任鏈模式的一種具體實現(xiàn)
trait Handler {
def handle(data:String){}
}
trait DataValidHandler extends Handler{
override def handle(data:String): Unit ={
println("CHeckData "+data)
super.handle(data)
}
}
trait SignValidHandler extends Handler{
override def handle(data:String): Unit ={
println("CHeckSIgn "+data)
super.handle(data)
}
}
class MyHandler(val name:String)extends SignValidHandler with DataValidHandler {
def sayHello = {
println("Hello,"+name)
handle(name)
}
}
val h1 = new MyHandler("Jack")
h1.sayHello
運行結果:
Hello,Jack
CHeckData Jack
CHeckSIgn Jack
2-3 在trait中覆蓋抽象方法
trait Logger {
def log(msg:String)
}
trait MyLogger extends Logger{
//覆蓋時,如果使用了super.方法時墩瞳,無法通過編譯驼壶,因為super調用的是抽象方法,
//此時子trait的方法還是被認為是抽象的喉酌,所以還需要加上abstract override修飾
// method log in trait Logger is accessed from super.It may not be abstract unless it is overridden by a member declared `abstract' and `override'
// override def log(msg:String){
// println("logs:" + msg)
// super.log(msg)
// }
abstract override def log(msg:String){
println("logs:" + msg)
super.log(msg)
}
}
trait MySubLogger extends Logger {
override def log(msg: String) {
println("MySubLogger logs:" + msg)
}
}
//類必須繼承實現(xiàn)了抽象方法的trait热凹,否則編譯報錯,報錯信息如下
//Error:(17, 7) class People needs to be a mixin, since method log in trait MyLogger of type (msg: String)Unit is marked `abstract' and `override', but no concrete implementation could be found in a base class
//class People extends MyLogger{
class People extends MySubLogger{
var data:String = "test"
def execLog = log(data)
}
object Main {
def main(args: Array[String]): Unit = {
var p = new People
p.execLog
}
}
2-4 混合使用trait的具體方法和抽象方法
trait Valid {
//在trait中可以混合使用具體方法和抽象方法
//可以讓具體方法依賴于抽象方法泪电,而通過具體類的方法來實現(xiàn)trait在抽象方法
//這種trait其實就是設計模式中的模板設計模式的體現(xiàn)
def getName:String
def valid:Boolean={
getName == "leo"
}
}
class Person(val name:String) extends Valid{
println(valid)
def getName = name
}
object Main{
def main(args: Array[String]): Unit = {
val p = new Person("leo")
}
}
2-5 trait的構造機制
在scala中般妙,trait也是有構造方法的,就是不包含在任何方法的代碼
而繼承了trait類的構造機制如下:
1相速、父類的構造函數(shù)執(zhí)行碟渺;
2、trait的構造代碼執(zhí)行和蚪,多個trait從左到右止状,依次執(zhí)行烹棉;
3攒霹、構造trait時,會先構造父trait浆洗,如果多個trait繼承同一個父trait催束,則父trait只會構造一次;
4伏社、所有的trait構造完畢之后抠刺,子類的構造函數(shù)執(zhí)行
class Person1 {println("Person`s constructor")}
trait Logger1{println("Logger`s constructor")}
trait Mylogger extends Logger1{println("Mylogger`s constructor")}
trait Timerlogger extends Logger1{println("Timerlogger`s constructor")}
class Student1 extends Person1 with Mylogger with Timerlogger{
println("Student`s constructor")
}
object Main{
def main(args: Array[String]): Unit = {
val p = new Student1()
}
}
運行結果如下:
Persons constructor Logger
s constructor
Myloggers constructor Timerlogger
s constructor
Student`s constructor
2-6 trait字段的初始化
在scala中塔淤,trait是沒有接收參數(shù)的構造函數(shù)的,這是trait與class的唯一區(qū)別速妖,但是如果需要trait對field進行初始化高蜂,只能使用scala中非常特殊的一種高級特性,--提前定義
trait sayHello{
val msg:String
println(msg.toString)
}
class Person extends sayHello{
val msg: String = "hello"
}
直接構造罕容,會先構造父類备恤,msg還沒初始化,就打印锦秒,會報空指針錯誤
val p = new Person //java.lang.NullPointerException
下面幾種方式則不會出錯:
創(chuàng)建實例的時候露泊,動態(tài)混入屬性,并賦值
//重新定義沒有屬性的類
class Person
//提前賦值
val p = new {
val msg:String = "init"
} with Person with sayHello
還有一種寫法
class Person extends {
val msg:String = "init"
} with sayHello
val p = new Person
還有一種寫法:
trait sayHello{
lazy val msg:String = null
println(msg.toString)
}
class Person extends sayHello{
override lazy val msg:String = "init"
}
object Main{
def main(args: Array[String]): Unit = {
val p = new Person
}
}
2-7 讓trait繼承類
在scala中旅择,trait也可以繼承class惭笑,此時class就會成為所有繼承該trait的類的父類
class MYUtil {
def printMessage(msg:String) = println(msg)
}
trait Logge extends MYUtil{
def log(msg:String)= printMessage(msg)
}
class Person(val name:String)extends Logge{
def sayHello: Unit ={
log("Hi,I'm "+name)
printMessage("Hi,I'm "+name)
}
}
object Main{
def main(args: Array[String]): Unit = {
val p = new Person("leo")
p.sayHello
}
}