Scala筆記整理(四):Scala面向對象—類詳解2(繼承相關)
單例
希望某個類只存在一個使用的對象,而不管有多少個調用者在使用它,就是單例的概念。
Java中的單例
package cn.xpleaf.single; /** * 單例模式-餓漢式(在屬性中先創建好對象,不管是否調用getInstance方法) * @author yeyonghao * */ public class User { private static User user = new User(); private User(){} //外界調用不到構造函數 public static User getInstance(){ return user; } } /** * 單例模式-懶漢式(屬性中先定義對象為null,只有第一次調用getInstance方法的時候才創建對象) * @author yeyonghao * */ public class User2 { public static User2 user = null; private User2(){} //外界調用不到構造函數 public static User2 getInstance(){ if(user == null){ synchronized(User2.class){ if(user == null) { user = new User2(); } } } return user; } }
Scala中的單例
代碼及說明如下:
package cn.xpleaf.bigdata.p3 /** * scala模擬單例 */ object _01SingletonOps { def main(args: Array[String]): Unit = { val s1 = Singleton val s2 = Singleton s1.incrementAge() println("s1‘s age is: " + s1.getAge) println("s2‘s age is: " + s2.getAge) println("=================================") println("s1‘s price is: " + s1.getPhone.getPrice) println("s2‘s price is: " + s2.getPhone.getPrice) println("s1 incrementPrice...") s1.getPhone.incrementPrice(500.0) println("s1‘s price is: " + s1.getPhone.getPrice) println("s2‘s price is: " + s2.getPhone.getPrice) } } /** * 因為scala中沒有靜態字段之類,所以我們只能使用static去模擬靜態 * 當被定義為object的類型,其中的所有程序都可以當作java中的靜態成員去對待 * 直接可以通過類名.去調用 */ object Singleton { private var age = 5 private var phone:Phone = new Phone def incrementAge(): Unit = { age += 1 } def getAge = age def getPhone = phone } class Phone { var price:Double = 1000.0 def incrementPrice(addPrice:Double): Unit = { this.price += price } def getPrice = price }
輸出結果如下:
s1‘s age is: 6
s2‘s age is: 6
=================================
s1‘s price is: 1000.0
s2‘s price is: 1000.0
s1 incrementPrice...
s1‘s price is: 2000.0
s2‘s price is: 2000.0
伴生類和伴生對象
直接看下面的代碼和說明:
package cn.xpleaf.bigdata.p3 /** * scala中的伴生對象和伴生類 * 因為scala的語法結構中沒有像java中的類,既可以擁有靜態方法,也可以有非靜態方法, * 所以就弄出這麽一種結構,和該類class同名的object,並且該class與其同名的object必須要放在同一個.scala文件中 * 那麽我們把這個object稱之為同名class的伴生對象 * 把該class稱之為同名object的伴生類 * * 其實我們之前學習的數組Array,Map等的創建都用到了其對應的伴生對象的操作方式。 * 既可以使用類來創建對象,也可以用伴生對象來創建對象 * 二者唯一差別在於使用伴生類創建對象,必須要有new,而使用伴生對象來創建對象,直接省去new * * 在伴生類和伴生對象中可以互相訪問對方的成員 * 要想讓伴生對象創建的對象也能訪問伴生類中的公開成員,必須要在伴生對象中復寫一個方法,apply * 返回值為該伴生類的對象(細節:在使用伴生對象創建對象的時候,必須要加(),apply方法也要加()) * * 如果一個class有伴生的object對象,那麽這個class有多少個構造函數,object就要定義多少個apply函數, * apply參數列表同構造函數的參數列表 */ object _02CompanionObjOps { def main(args: Array[String]): Unit = { var arr1 = new Array[Int](3) // 伴生類方式 var arr2 = Array[Int](4) // 伴生對象方式 /** * 所以可以知道,前面使用Array或者Map時,之所以用new行,不用new也行,是因為其使用了伴生類和伴生對象的思想 */ val p = new Person val p1 = new Person() println(p1.getAge) // 復雜構造 val p2 = Person("張三", 14) println(p2.getAge) val p3 = Person("王五") println(p3.getAge) Person.show // p3.show // 該方法被定義在object中,不能這樣進行調用 } } class Person { private var name:String = _ private var age = 5 def this(name:String, age:Int) { this() this.name = name this.age = age } def this(name:String) { this() this.name = name } def setAge(age:Int): Unit = { this.age = age } def getAge = age } object Person { def show() = println("person") // 相當於模擬java中一個類的靜態方法,不同的是,需要使用Person.show來調用,而不可以使用p3.show來調用 def apply(): Person = new Person() def apply(name:String, age:Int): Person = new Person(name, age) def apply(name: String): Person = new Person(name) }
輸出結果如下:
5
14
5
person
object的繼承和App
代碼及說明如下:
package cn.xpleaf.bigdata.p3
/**
* 對象的擴展
* 只有方法的定義,沒有方法體,該方法被稱之為抽象方法
* 定義了抽象方法的類,必須是抽象類
* scala中定義的抽象方法可以省略abstract關鍵字(實際上測試是不能加abstract的)
*
* object的另外一個特性,scala中程序的執行都必須要通過main
* 當然有時候我們可以不用寫這個main,只需要繼承一個App即可
*/
object ObjectExtendsOps extends App {
/*def main(args: Array[String]): Unit = {
ObjAccountImpl.register("xpleaf")
}*/
if(args == null || args.length < 2) { // 繼承App後,也是可以直接使用args對象的
println(
"""參數要寫正確哦. Usage: <inputPath string> <outputPath string>
|inputPath: 輸入文件路徑
|outputPath: 輸出文件路徑
""".stripMargin)
System.exit(-1)
}
println(args(0))
println(args(1))
ObjAccountImpl.register("xpleaf")
}
abstract class Account(name:String) {
def register(name:String)
def unRegister(name:String)
}
object ObjAccountImpl extends Account("張三") {
override def register(name: String): Unit = {
println(name + "註冊賬號")
}
override def unRegister(name: String): Unit = {
println(name + "註銷賬號")
}
}
/**
* 如果是類繼承的話,則可以加上構造器,但是object去繼承時就不行
*/
class ObjAccountImpl1(name:String) extends Account(name) {
override def register(name: String): Unit = {
println(name + "註冊賬號")
}
override def unRegister(name: String): Unit = {
println(name + "註銷賬號")
}
}
輸出結果如下:
inputPath
outputPath
xpleaf註冊賬號
object模擬枚舉
Java中的枚舉
使用枚舉來實現一周,同時使用抽象類來模擬實現枚舉,完整的案例如下:
package cn.xpleaf.bigdata.p2;
public class EnumOps {
public static void main(String[] args) {
/*TrafficLight green = TrafficLight.GREEN;
TrafficLight red = TrafficLight.RED;
TrafficLight yellow = TrafficLight.YELLOW;
System.out.println(green); // GREEN
*/
Week mon1 = Week.MON;
System.out.println(mon1); // MON
System.out.println(mon1.next());
WeekDay mon2 = WeekDay.MON;
System.out.println(mon2); // MON
System.out.println(mon2.next());
}
}
/**
* 創建java中的一個枚舉
* java中的枚舉就是枚舉類的一個在本類中定義的本類實例對象
*/
enum TrafficLight {
RED("RED"),
YELLOW("YELLOW"),
GREEN("GREEN");
// private TrafficLight() {}
String color;
TrafficLight(String color) {
this.color = color;
}
}
/**
* 枚舉實現的一周
*/
enum Week {
MON("MON") {
@Override
public Week next() {
return Week.TUE;
}
@Override
public Week previous() {
return Week.SUN;
}
},
TUE("TUE") {
@Override
public Week next() {
return Week.WED;
}
@Override
public Week previous() {
return Week.MON;
}
},
WED("WED") {
@Override
public Week next() {
return Week.THR;
}
@Override
public Week previous() {
return Week.TUE;
}
},
THR("THR") {
@Override
public Week next() {
return Week.WED;
}
@Override
public Week previous() {
return Week.FRI;
}
},
FRI("FRI") {
@Override
public Week next() {
return Week.THR;
}
@Override
public Week previous() {
return Week.SAT;
}
},
SAT("SAT") {
@Override
public Week next() {
return Week.SUN;
}
@Override
public Week previous() {
return Week.FRI;
}
},
SUN("SUN") {
@Override
public Week next() {
return Week.MON;
}
@Override
public Week previous() {
return Week.SAT;
}
};
String weekDay;
Week(String weekDay) {
this.weekDay = weekDay;
}
// 要想實現下面兩個方法的效果,則上面的所有元素都需要重寫
public Week next() {
return null;
}
public Week previous() {
return null;
}
}
/**
* 用抽象類來模擬枚舉
*/
abstract class WeekDay {
public static WeekDay MON = new WeekDay("MON") {
@Override
public String toString() {
return this.weekDay;
}
WeekDay next() {
return TUE;
}
WeekDay previous() {
return SUN;
}
};
public static WeekDay TUE = new WeekDay("TUE") {
@Override
public String toString() {
return this.weekDay;
}
WeekDay next() {
return WED;
}
WeekDay previous() {
return TUE;
}
};
public static WeekDay WED = new WeekDay("WEB") {
@Override
public String toString() {
return this.weekDay;
}
WeekDay next() {
return THU;
}
WeekDay previous() {
return TUE;
}
};
public static WeekDay THU = new WeekDay("THU") {
@Override
public String toString() {
return this.weekDay;
}
WeekDay next() {
return FRI;
}
WeekDay previous() {
return WED;
}
};
public static WeekDay FRI = new WeekDay("FRI") {
@Override
public String toString() {
return this.weekDay;
}
WeekDay next() {
return SAT;
}
WeekDay previous() {
return THU;
}
};
public static WeekDay SAT = new WeekDay("SAT") {
@Override
public String toString() {
return this.weekDay;
}
WeekDay next() {
return SUN;
}
WeekDay previous() {
return FRI;
}
};
public static WeekDay SUN = new WeekDay("SUN") {
@Override
public String toString() {
return this.weekDay;
}
WeekDay next() {
return MON;
}
WeekDay previous() {
return SAT;
}
};
String weekDay;
private WeekDay(String weekDay) {
this.weekDay = weekDay;
}
@Override
public String toString() {
return weekDay;
}
abstract WeekDay next();
abstract WeekDay previous();
}
scala中的枚舉
案例及說明如下:
package cn.xpleaf.bigdata.p3
/**
* 使用object去模擬枚舉
* 枚舉,其實說白了就是將一個有限的集合中所有的可能都羅列出來,不用再人為進行輸入,就通過
* 枚舉類來完成,規範我們的編程
* 在java中有專門的枚舉這種類型,是和class、interface是並列的,是enum
*
* scala中枚舉的類是類名.Value,是因為要繼承一個Enumeration的特質,而不像java可以直接定義類的結構enum
*
*/
object _04EnumOps {
def main(args: Array[String]): Unit = {
val red = TrafficLight.RED // red的類型為:TrafficLight.Value
println(red) // RED
val red1 = TrafficLight.RED1 // 就是本object的引用,如這裏,red1的"類型"為:TrafficLight.type,即對TrafficLight的引用
println(red1) // TrafficLight
}
}
object TrafficLight extends Enumeration {
// val RED = TrafficLight;
// val YELLOW = TrafficLight;
// val GREEN = TrafficLight;
val RED, YELLOW, GREEN = Value
val RED1 = TrafficLight
}
類的繼承
直接看代碼和註釋。
實例1
package cn.xpleaf.bigdata.p3.extendz
/**
* scala中的繼承
* 描述類與類之間的關系,其中繼承是一種,反映的是is a這種關系,一個類是另外一種類型
* 子類通過關鍵字extends繼承了父類的field和method,同時可以自定義對應的field和method
*
* 需要註意的是,如果父類中的成員,包括父類被final修飾了,則無法被繼承,覆蓋
* 同樣的,如果父類成員被private修飾之後,子類也無法繼承或是覆蓋
*
* 當有繼承體系的時候,在用子類構造器創建對象的時候,先調用父類的構造器,
*
* 子類調用父類成員,如果子類沒有重名的,直接成員名,如果有重名的,需要使用super. --->有重名的,在父類中定義方法,通過super.方法名調用即可
* scala中是無法直接調用父類構造器,是間接調用父類構造器,只能是主構造器調用父類的構造器,輔助構造器是不能直接調用父類的構造器
* (實際測試是:無法使用super的方式訪問父類的字段,可以這樣訪問父類的方法,
另外也沒有辦法調用父類構造器,不管在哪裏,當然在實例中的,在定義子類的時候,那種方式也算是
子類主構造器對父類構造器的調用,因為在子類構造器中,確實是沒有辦法使用super的方式對父類構造器進行調用
* 當然現在學得還十分淺顯,後面肯定會深究一下
* )
*/
object _01ExtendsOps {
def main(args: Array[String]): Unit = {
val stu = new Student("001A")
stu.show()
}
}
class Person {
println("----person primary constructor----")
var name:String = "張三"
var age:Int = 0
def this(name:String, age:Int) {
this()
this.name = name
this.age = age
}
/*final*/ def show() = println(s"name is $name, and age is $age")
}
class Student extends Person {
println("----student primary constructor----")
// override var name:String = "小紅" // 父類中有該字段,不能再重新定義,加上override,編譯正常,但運行時也會有異常
private var cardID:String = _
def this(cardID:String) {
this()
this.cardID = cardID
println("----student 輔助 constructor----")
}
/*
為啥被final修飾的成員,子類無法覆蓋?
因為final代表的就是常量,代表的就是最終的量,所以不能進行覆蓋
*/
override def show(): Unit = {
super.show()
println("name is " + name + s", and age is $age, cardID is $cardID")
}
}
輸出結果如下:
----person primary constructor----
----student primary constructor----
----student 輔助 constructor----
name is 張三, and age is 0
name is 張三, and age is 0, cardID is 001A
實例2
package cn.xpleaf.bigdata.p3.extendz
/**
* scala繼承
* scala向父類的構造器傳遞參數
* java中多態,
* 父類引用指向子類對象
* Fu zi = new zi();
* scala中多態的體現
* val zi:Fu = new zi()
* 編譯看=的左邊,運行要看=的右側
*/
object _02ExtendsOps {
def main(args: Array[String]): Unit = {
val coder:Worker = new Coder("xpleaf", 23, "scala")
coder.show()
}
}
class Worker(name:String, age:Int) {
var language:String = _
def show(): Unit = {
println(s"name is $name,, and age is $age")
}
}
class Coder(name:String, age:Int, language:String) extends Worker(name, age) {
def this() {
this("程序員", 18, "Java")
}
override def show(): Unit = {
println(s"name is $name, and age is $age, language is $language")
}
}
輸出結果如下:
name is xpleaf, and age is 23, language is scala
總結
1、一個類有一個主構造器和任意數量的輔助構造器,而每個輔助構造器都必須以對先前定義的輔助構造器或主構造器的調用開始。這樣做帶來的後果是,輔助構造器永遠都不可能直接調用超類的構造器。
2、子類的輔助構造器最終都會調用主構造器,只有主構造器可以調用超類的構造器。主構造器是和類定義交織在一起的,調用超類構造器的方式也同樣交織在一起。
3、註意!如果是父類中接收的參數,比如name和age,子類中接收時,就不要用任何val或var來修飾了,否則會認為是子類要覆蓋父類的field
class Person (val name:String, val age:Int)
class Employee(name:String, age:Int, val address:String) extends Person(name,age){
def this(name:String){
this(name,0,"")
}
def this(age:Int){
this("garry",age,"")
}
}
類型檢查和類型轉換
測試代碼如下:
package cn.xpleaf.bigdata.p3.extendz
/**
* 在java中判斷對象A是否為B這種類型
* if(A instanceOf B){
*
* }
* 常見在於equals裏面來進行操作
* 類型檢查使用instanceOf,類型轉換使用的強制類型轉換--->Java
* 而在scala中:
* 類型檢查:isInstanceOf
* 類型轉換:asInstanceOf
*
* 這裏書寫的這種isInstanceOf也可以使用scala中模式匹配來操作
*/
object _03ExtendsOps {
def main(args: Array[String]): Unit = {
// 比較兩個工程師是否為同一個人,依據就是姓名是否相等
val e1 = new Engineer("lisi", 12345)
val e2 = new Engineer("lisi", 12345)
println(e1.equals(e2)) // true
println(e1.getName) // lisi
println("======================================")
// 類型檢查的第二種方式,使用模式匹配來進行操作
val e3:Employee = new Engineer("wangwu", 3457)
val e4:Employee = new ProductManager
// 使用模式匹配來確定要執行的方法
e3 match {
case e:Engineer => {
e.code()
}
case e:ProductManager => {
e.pm()
}
}
}
}
class Employee {
private var name:String = _
private var salary:Float = _
def this(name:String, salary:Float) {
this()
this.name = name
this.salary = salary
}
def getName = name
def show: Unit = {
println(s"$name ======> $salary")
}
}
class Engineer(name:String, salary:Float) extends Employee {
private var age:Int = _
override def getName = name
def code() = println("Engineer正在寫代碼...")
override def equals(obj: scala.Any): Boolean = {
if(!obj.isInstanceOf[Engineer]) { // 類型檢查
false
} else {
// 類型轉換
val o = obj.asInstanceOf[Engineer]
if(this.getName.equalsIgnoreCase(o.getName)) {
true
} else {
false
}
}
}
}
class ProductManager extends Employee {
def pm() = println("pm正在趕需求...")
}
輸出結果如下:
true
lisi
======================================
Engineer正在寫代碼...
protected關鍵字
測試代碼如下:
package cn.xpleaf.bigdata.p3.extendz
/**
* scala繼承之protected修飾符
* 1.被protected修飾的成員,子類可以直接訪問,其它子類實例可以訪問
* 2.如果在protected後面加上[this]則表示只能在當前子類中訪問,不能在其它子類實例中進行訪問,即可能在子類的定義中使用,子類對象無法調用
* 3.如果protected或者private後面[]中的內容不是this,而是其它包名,則說明該字段或者該方法
* 只能在對應的包目錄或者子包中目錄結構中能夠進行調用
*/
object _04ExtendsOps {
def main(args: Array[String]): Unit = {
val wc = new Dog
val fg = new Dog
wc.makeDifference(fg)
}
}
class Animal {
private[this] var privateThis:Int = 4
private[extendz] var privatePackage:Int = 4
protected var leg:Int = 4
protected[this] var eyes:Int = 2
protected[extendz] var others:Int = 2
}
class Dog extends Animal {
private var color:String = "yellow"
def getEyes:Int = eyes
def makeDifference(dog:Dog): Unit = {
println(s"this dog‘s leg is $leg, that dog‘s leg is ${dog.leg}") // 可以直接使用dog.leg,因為protected修飾
println(s"this dogs‘s eyes is $eyes, that dog‘s eyes is ${dog.getEyes}") // 不可以直接使用dog.eyes,因為protected[this]修飾,只能本類或子類使用
println(s"this dog‘s others is $others, that dog‘s others is ${dog.others}") // others被protected[extendz]修飾,extendz包下的都可以訪問
// println(privateThis) // 不能訪問
println(privatePackage + "\t" + dog.privatePackage) // 可以訪問
// 可以看到scala中的修飾符跟Java的還是有區別的
}
}
輸出結果如下:
this dog‘s leg is 4, that dog‘s leg is 4
this dogs‘s eyes is 2, that dog‘s eyes is 2
this dog‘s others is 2, that dog‘s others is 2
4 4
匿名內部類
實例
測試代碼如下:
package cn.xpleaf.bigdata.p3.extendz
/**
* scala中的匿名內部類
* 第一:匿名內部類是一個內部類
* 第二:這個內部類沒有名字
* 沒有定義的過程,只在程序運行時臨時定義
*
* 往往匿名內部類用的時候,該內部類對應的類一般是一個抽象,同時該類中抽象方法一般不超過3個
*
* 對於如果只調用一次的類而言,我們一般就可以使用匿名內部類的方式去調用,沒有必要創建一個類的定義
*/
object _05AnonymousInnerOps {
def main(args: Array[String]): Unit = {
val cat = new Cat
cat.fetchMouse()
//
val jqCat = new Cat() {
def code() = println(s"$kind\5$color\t正在寫代碼...")
}
jqCat.fetchMouse()
jqCat.code()
//
val outer:Outer = new Outer {
override def show1(): Unit = {
println("impl show1")
}
override def show2(): Unit = {
println("impl show2")
}
}
outer.show1()
outer.show2()
}
}
class Cat {
var color:String = "黑貓"
val eyes:Int = 2
var name:String = _
var kind:String = "波斯"
def fetchMouse() = println(s"$kind, color‘s $color")
}
abstract class Outer {
def show1()
def show2()
}
輸出結果如下:
波斯, color‘s 黑貓
波斯, color‘s 黑貓
波斯 黑貓 正在寫代碼...
impl show1
impl show2
總結
1、在Scala中,匿名子類是非常常見,而且非常強大,因為Spark源碼中也大量使用了這種匿名子類。
2、匿名子類,也就是說,可以定義一個類的沒有名稱的子類,並直接創建其對象,然後將對象的引用賦予一個變量。之後甚至可以將該匿名子類的對象傳遞給其他函數
3、
class Person(val name:String){
def greeting = "Glad to see you " + name
}
val person = newPerson ("Garry") {
override def greeting ="Greet4ings, Earthling! My name is . " + name
}
println(person.greeting)
從技術上講,這將會創建出一個結構類型的對象。該類型標記為Person{ def greeting:String }
。
4、也可以用這個類型作為參數類型的定義:
def greetingMeet (p: Person { def greeting:String}) {
println(p.name + " says: " + p.greeting)
}
抽象
抽象類
1、和Java一樣,你可以用abstract關鍵字來標記不能被實例化的類,通常這是因為它的某個或某幾個方法沒有被完整定義
2、
abstract classPerson(val name: String) {
def greeting: String // 沒有方法體,這是一個抽象方法
}
在Scala中,不像Java,你不需要對抽象方法使用abstract關鍵字,你只是省去其方法體。但和Java一樣,如果某個類至少存在一個抽象方法,則該類必須聲明為abstract
3、在子類中重寫超類的抽象方法時,你不需要使用override關鍵字。
abstract classPerson(val name: String) {
def greeting: String // 沒有方法體,這是一個抽象方法
}
classEmployee(name:String) extends Person(name){
def greeting:String = {
println("name=> " + name)
"Hello => " + name
}
}
val employee = newEmployee("Garry")
println(employee.greeting)
抽象字段
1、除了抽象方法外,類還可以擁有抽象字段。抽象字段就是一個沒有初始值的字段。
abstract classPerson {
val personID : Int // 沒有初始化,這是一個帶有抽象的getter方法的抽象字段
var personName : String // 另一個抽象字段,帶有抽象的getter和setter方法
}
該類為personID和personName字段定義了抽象的getter方法,為name字段定義了抽象的setter方法。
2、生成的Java類並不帶字段,具體的子類必須提供具體的字段,例如:
abstract classPerson {
val personID : Int // 沒有初始化,這是一個帶有抽象的getter方法的抽象字段
var personName : String // 另一個抽象字段,帶有抽象的getter和setter方法
}
class Employee(val personID: Int) extendsPerson { // 子類有具體的id屬性
println("personID=> " + personID)
var personName = "Garry" // 和具體的name屬性
}
val employee = new Employee(100)
println(employee.personName)
實例
測試代碼如下:
package cn.xpleaf.bigdata.p3.extendz
/**
* scala中的抽象
* 抽象的字段,就是只有字段定義,沒有字段初始化的字段
* 當一個類中的字段為抽象字段的話,該類要麽abstract,要麽實現繼承一個含有對應抽象字段的類
*
* 多態的應用時:
* 抽象的字段被定義為val,則只能讀,不能寫(一旦初始化,不能修改)
* 子類也只能覆蓋為val
* 抽象字段被定義為var,是普通變量
* 子類可以覆蓋為var,也可以覆蓋為val
* 但是如果覆蓋為val的話,只能讀不能修改
*/
object _06AbstractOps {
def main(args: Array[String]): Unit = {
val p:AbsPerson = new ZiPerson
println("age: " + p.age)
println("name: " + p.name)
}
}
abstract class AbsPerson {
val age:Int
var name:String
}
class ZiPerson extends AbsPerson {
val age: Int = 10
var name: String = "張三"
}
輸出結果如下:
age: 10
name: 張三
特質
測試代碼如下:
package cn.xpleaf.bigdata.p3.extendz
/**
* scala特質說明
* scala中同樣像java一樣不能實現多繼承,只能通過多實現來彌補,同時多實現,要比多繼承靈活的多
* 在java中的實現,我們稱之為接口interface,使用關鍵字implement
* 在scala中,我們稱之為特質trait,使用關鍵字extends
* 如果在java中實現的是多個接口,接口之間使用","隔開
* 如果在scala中繼承的是多個特質,特質之間使用with隔開
*
* trait是一個比較特殊的結構
* 既可以有抽象方法,也可以有非抽象方法,如果trait中的方法都是抽象的,我們就可以將其看作java的接口
* 當我們擴展的多個特質,擁有相同的方法的時候,默認只會調用最右面一個特質的方法
*
* 要求:
* 如果繼承的多個特質含有相同方法,我們想執行每一個方法,怎麽解決?
* 則在繼承的特質後面追加一個super.方法即可
* 註意,當前根特質中的方法必須不能為抽象
* 當擴展的特質是多個的話,執行順序是從右往左
*/
object _07TraitOps {
def main(args: Array[String]): Unit = {
val cLog:Log = new ConsoleLog
cLog.log("日誌信息")
println("==================")
val mLog:MainLog = new MainLog
mLog.log("info")
}
}
/**
* 定義了一個特質log,有一個抽象的方法log
*/
trait Log {
def log(msg:String): Unit = {
}
}
class ConsoleLog extends Log {
override def log(msg: String): Unit = {
println(s"將 ${msg} 輸出到Console")
}
}
trait FileLog extends Log {
override def log(msg: String): Unit = {
println(s"將 ${msg} 輸出到File")
super.log(msg)
}
}
trait FlumeLog extends Log {
override def log(msg: String): Unit = {
println(s"將 ${msg} 采集到Flume")
super.log(msg)
}
}
class MainLog extends FileLog with FlumeLog {
override def log(msg: String): Unit = {
super.log(msg)
}
}
輸出結果如下:
將 日誌信息 輸出到Console
==================
將 info 采集到Flume
將 info 輸出到File
Scala筆記整理(四):Scala面向對象—類詳解2(繼承相關)