spark筆記之類、對象、繼承、特質
1.1. 類1.1.1. 類的定義
package cn.itcast.class_demo /** * 在Scala中,類並不用聲明為public類型的。 * Scala源文件中可以包含多個類,所有這些類都具有共有可見性。 */ class Person { //用val修飾的變量是可讀屬性,有getter但沒有setter(相當與Java中用final修飾的變量) val id="9527" //用var修飾的變量都既有getter,又有setter var age:Int=18 //類私有字段,只能在類的內部使用或者伴生對象中訪問 private var name : String = "唐伯虎" //類私有字段,訪問權限更加嚴格的,該字段在當前類中被訪問 //在伴生對象裏面也不可以訪問 private[this] var pet = "小強" } //伴生對象(這個名字和類名相同,叫伴生對象) object Person{ def main(args: Array[String]): Unit = { val p=new Person //如果是下面的修改,發現下面有紅線,說明val類型的不支持重新賦值,但是可以獲取到值 //p.id = "123" println(p.id) //打印age println(p.age) //打印name,伴生對象中可以在訪問private變量 println(p.name) //由於pet字段用private[this]修飾,伴生對象中訪問不到pet變量 //p.pet(訪問不到) } }
1.1.2. 構造器
Scala中的每個類都有主構造器,主構造器的參數直接放置類名後面,與類交織在一起。
註意:主構造器會執行類定義中的所有語句。
package cn.itcast.class_demo /** *每個類都有主構造器,主構造器的參數直接放置類名後面,與類交織在一起 */ class Student(val name:String,var age:Int) { //主構造器會執行類定義的所有語句 println("執行主構造器") private var gender="male" def this(name:String,age:Int,gender:String){ //每個輔助構造器執行必須以主構造器或者其他輔助構造器的調用開始 this(name,age) println("執行輔助構造器") this.gender=gender } } object Student { def main(args: Array[String]): Unit = { val s1=new Student("zhangsan",20) val s2=new Student("zhangsan",20,"female") } }
1.2. Scala面向對象編程之對象1.2.1. Scala中的object
? object 相當於 class 的單個實例,通常在裏面放一些靜態的 field 或者 method;
在Scala中沒有靜態方法和靜態字段,但是可以使用object這個語法結構來達到同樣的目的。
object作用:
1.存放工具方法和常量
2.高效共享單個不可變的實例
3.單例模式
? 舉例說明:
package cn.itcast.object_demo import scala.collection.mutable.ArrayBuffer class Session{} object SessionFactory{ //該部分相當於java中的靜態塊 val session=new Session //在object中的方法相當於java中的靜態方法 def getSession(): Session ={ session } } object SingletonDemo { def main(args: Array[String]) { //單例對象,不需要new,用【單例對象名稱.方法】調用對象中的方法 val session1 = SessionFactory.getSession() println(session1) //單例對象,不需要new,用【單例對象名稱.變量】調用對象中成員變量 val session2=SessionFactory.session println(session2) } }
1.2.2. Scala中的伴生對象
? 如果有一個class文件,還有一個與class同名的object文件,那麽就稱這個object是class的伴生對象,class是object的伴生類;
? 伴生類和伴生對象必須存放在一個.scala文件中;
? 伴生類和伴生對象的最大特點是,可以相互訪問;
? 舉例說明:
package cn.itcast.object_demo
//伴生類
class Dog {
val id = 1
private var name = "itcast"
def printName(): Unit ={
//在Dog類中可以訪問伴生對象Dog的私有屬性
println(Dog.CONSTANT + name )
}
}
//伴生對象
object Dog {
//伴生對象中的私有屬性
private val CONSTANT = "汪汪汪 : "
def main(args: Array[String]) {
val p = new Dog
//訪問私有的字段name
p.name = "123"
p.printName()
}
}
//執行結果 汪汪汪 : 123
1.2.3. Scala中的apply方法
? object 中非常重要的一個特殊方法,就是apply方法;
? apply方法通常是在伴生對象中實現的,其目的是,通過伴生類的構造函數功能,來實現伴生對象的構造函數功能;
? 通常我們會在類的伴生對象中定義apply方法,當遇到類名(參數1,...參數n)時apply方法會被調用;
? 在創建伴生對象或伴生類的對象時,通常不會使用new class/class() 的方式,而是直接使用 class(),隱式的調用伴生對象的 apply 方法,這樣會讓對象創建的更加簡潔;
? 舉例說明:
package cn.itcast.object_demo
/**
* Array 類的伴生對象中,就實現了可接收變長參數的 apply 方法,
* 並通過創建一個 Array 類的實例化對象,實現了伴生對象的構造函數功能
*/
// 指定 T 泛型的數據類型,並使用變長參數 xs 接收傳參,返回 Array[T] 數組
// 通過 new 關鍵字創建 xs.length 長的 Array 數組
// 其實就是調用Array伴生類的 constructor進行 Array對象的初始化
// def apply[T: ClassTag](xs: T*): Array[T] = {
// val array = new Array[T](xs.length)
// var i = 0
// for (x <- xs.iterator) { array(i) = x; i += 1 }
// array
// }
object ApplyDemo {
def main(args: Array[String]) {
//調用了Array伴生對象的apply方法
//def apply(x: Int, xs: Int*): Array[Int]
//arr1中只有一個元素5
val arr1 = Array(5)
//new了一個長度為5的array,數組裏面包含5個null
var arr2 = new Array(5)
println(arr1.toBuffer)
}
}
1.2.4. Scala中的main方法
? 同Java一樣,如果要運行一個程序,必須要編寫一個包含 main 方法的類;
? 在 Scala 中,也必須要有一個 main 方法,作為入口;
? Scala 中的 main 方法定義為 def main(args: Array[String]),而且必須定義在 object 中;
? 除了自己實現 main 方法之外,還可以繼承 App Trait,然後,將需要寫在 main 方法中運行的代碼,直接作為 object 的 constructor 代碼即可,而且還可以使用 args 接收傳入的參數;
? 案例說明:
package cn.itcast.object_demo
//1.在object中定義main方法
object Main_Demo1 {
def main(args: Array[String]) {
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World!")
}
}
}
//2.使用繼承App Trait ,將需要寫在 main 方法中運行的代碼
// 直接作為 object 的 constructor 代碼即可,
// 而且還可以使用 args 接收傳入的參數。
object Main_Demo2 extends App{
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World!")
}
}
1.3. Scala面向對象編程之繼承1.3.1. Scala中繼承(extends)的概念
? Scala 中,讓子類繼承父類,與 Java 一樣,也是使用 extends 關鍵字;
? 繼承就代表,子類可繼承父類的 field 和 method ,然後子類還可以在自己的內部實現父類沒有的,子類特有的 field 和method,使用繼承可以有效復用代碼;
? 子類可以覆蓋父類的 field 和 method,但是如果父類用 final 修飾,或者 field 和 method 用 final 修飾,則該類是無法被繼承的,或者 field 和 method 是無法被覆蓋的。
? private 修飾的 field 和 method 不可以被子類繼承,只能在類的內部使用;
? field 必須要被定義成 val 的形式才能被繼承,並且還要使用 override 關鍵字。 因為 var 修飾的 field 是可變的,在子類中可直接引用被賦值,不需要被繼承;即 val 修飾的才允許被繼承,var 修飾的只允許被引用。繼承就是改變、覆蓋的意思。
? Java 中的訪問控制權限,同樣適用於 Scala
? 舉例說明:
package cn.itcast.extends_demo
class Person {
val name="super"
def getName=this.name
}
class Student extends Person{
//繼承加上關鍵字
override
val name="sub"
//子類可以定義自己的field和method
val score="A"
def getScore=this.score
}
1.3.2. Scala中override 和 super 關鍵字
? Scala中,如果子類要覆蓋父類中的一個非抽象方法,必須要使用 override 關鍵字;子類可以覆蓋父類的 val 修飾的field,只要在子類中使用 override 關鍵字即可。
? override 關鍵字可以幫助開發者盡早的發現代碼中的錯誤,比如, override 修飾的父類方法的方法名拼寫錯誤。
? 此外,在子類覆蓋父類方法後,如果在子類中要調用父類中被覆蓋的方法,則必須要使用 super 關鍵字,顯示的指出要調用的父類方法。
? 舉例說明:
package cn.itcast.extends_demo
class Person1 {
private val name = "leo"
val age=50
def getName = this.name
}
class Student extends Person1{
private val score = "A"
//子類可以覆蓋父類的 val field,使用override關鍵字
override
val age=30
def getScore = this.score
//覆蓋父類非抽象方法,必須要使用 override 關鍵字
//同時調用父類的方法,使用super關鍵字
override def getName = "your name is " + super.getName
}
1.3.3. Scala中isInstanceOf 和 asInstanceOf
如果實例化了子類的對象,但是將其賦予了父類類型的變量,在後續的過程中,又需要將父類類型的變量轉換為子類類型的變量,應該如何做?
? 首先,需要使用 isInstanceOf 判斷對象是否為指定類的對象,如果是的話,則可以使用 asInstanceOf 將對象轉換為指定類型;
? 註意: p.isInstanceOf[XX] 判斷 p 是否為 XX 對象的實例;p.asInstanceOf[XX] 把 p 轉換成 XX 對象的實例
? 註意:如果沒有用 isInstanceOf 先判斷對象是否為指定類的實例,就直接用 asInstanceOf 轉換,則可能會拋出異常;
? 註意:如果對象是 null,則 isInstanceOf 一定返回 false, asInstanceOf 一定返回 null;
? Scala與Java類型檢查和轉換
Scala
Java
obj.isInstanceOf[C]
obj instanceof C
obj.asInstanceOf[C]
(C)obj
classOf[C]
C.class
? 舉例說明:
package cn.itcast.extends_demo
class Person3 {}
class Student3 extends Person3
object Student3{
def main (args: Array[String] ) {
val p: Person3 = new Student3
var s: Student3 = null
//如果對象是 null,則 isInstanceOf 一定返回 false
println (s.isInstanceOf[Student3])
// 判斷 p 是否為 Student3 對象的實例
if (p.isInstanceOf[Student3] ) {
//把 p 轉換成 Student3 對象的實例
s = p.asInstanceOf[Student3]
}
println (s.isInstanceOf[Student3] )
}
}
1.3.4. Scala中getClass 和 classOf
? isInstanceOf 只能判斷出對象是否為指定類以及其子類的對象,而不能精確的判斷出,對象就是指定類的對象;
? 如果要求精確地判斷出對象就是指定類的對象,那麽就只能使用 getClass 和 classOf 了;
? p.getClass 可以精確地獲取對象的類,classOf[XX] 可以精確的獲取類,然後使用 == 操作符即可判斷;
? 舉例說明:
package cn.itcast.extends_demo
class Person4 {}
class Student4 extends Person4
object Student4{
def main(args: Array[String]) {
val p:Person4=new Student4
//判斷p是否為Person4類的實例
println(p.isInstanceOf[Person4])//true
//判斷p的類型是否為Person4類
println(p.getClass == classOf[Person4])//false
//判斷p的類型是否為Student4類
println(p.getClass == classOf[Student4])//true
}
}
1.3.5. Scala中使用模式匹配進行類型判斷
? 在實際的開發中,比如 spark 源碼中,大量的地方使用了模式匹配的語法進行類型的判斷,這種方式更加地簡潔明了,而且代碼的可維護性和可擴展性也非常高;
? 使用模式匹配,功能性上來說,與 isInstanceOf 的作用一樣,主要判斷是否為該類或其子類的對象即可,不是精準判斷。
? 等同於 Java 中的 switch case 語法;
? 舉例說明:
package cn.itcast.extends_demo
class Person5 {}
class Student5 extends Person5
object Student5{
def main(args: Array[String]) {
val p:Person5=new Student5
p match {
// 匹配是否為Person類或其子類對象
case per:Person5 => println("This is a Person5‘s Object!")
// 匹配所有剩余情況
case _ =>println("Unknown type!")
}
}
}
1.3.6. Scala中protected
? 跟 Java 一樣,Scala 中同樣可使用 protected 關鍵字來修飾 field 和 method。在子類中,可直接訪問父類的 field 和 method,而不需要使用 super 關鍵字;
? 還可以使用 protected[this] 關鍵字, 訪問權限的保護範圍:只允許在當前子類中訪問父類的 field 和 method,不允許通過其他子類對象訪問父類的 field 和 method。
? 舉例說明:
package cn.itcast.extends_demo
class Person6{
protected var name:String="tom"
protected[this] var hobby:String ="game"
protected def sayBye=println("再見...")
}
class Student6 extends Person6{
//父類使用protected 關鍵字來修飾 field可以直接訪問
def sayHello =println("Hello "+name)
//父類使用protected 關鍵字來修飾method可以直接訪問
def sayByeBye=sayBye
def makeFriends(s:Student6)={
println("My hobby is "+hobby+", your hobby is UnKnown")
}
}
object Student6{
def main(args: Array[String]) {
val s:Student6=new Student6
s.sayHello
s.makeFriends(s)
s.sayByeBye
}
}
1.3.7. Scala中調用父類的constructor
? Scala中,每個類都可以有一個主constructor和任意多個輔助constructor,而且每個輔助constructor的第一行都必須調用其他輔助constructor或者主constructor代碼;因此子類的輔助constructor是一定不可能直接調用父類的constructor的;
? 只能在子類的主constructor中調用父類的constructor。
? 如果父類的構造函數已經定義過的 field,比如name和age,子類再使用時,就不要用 val 或 var 來修飾了,否則會被認為,子類要覆蓋父類的field,且要求一定要使用 override 關鍵字。
? 舉例說明:
package cn.itcast.extends_demo
class Person7(val name:String,val age:Int){
var score :Double=0.0
var address:String="beijing"
def this(name:String,score:Double)={
//每個輔助constructor的第一行都必須調用其他輔助constructor或者主constructor代碼
//主constructor代碼
this(name,30)
this.score=score
}
//其他輔助constructor
def this(name:String,address:String)={
this(name,100.0)
this.address=address
}
}
class Student7(name:String,score:Double) extends Person7(name,score)
1.3.8. Scala中匿名內部類
? 在Scala中,匿名內部類是非常常見的,而且功能強大。Spark的源碼中大量的使用了匿名內部類;
? 匿名內部類,就是定義一個沒有名稱的子類,並直接創建其對象,然後將對象的引用賦予一個變量,即匿名內部類的實例化對象。然後將該對象傳遞給其他函數使用。
? 舉例說明:
package cn.itcast.extends_demo
class Person8(val name:String) {
def sayHello="Hello ,I‘m "+name
}
class GreetDemo{
//接受Person8參數,並規定Person8類只含有一個返回String的sayHello方法
def greeting(p:Person8{
def sayHello:String})={
println(p.sayHello)
}
}
object GreetDemo {
def main(args: Array[String]) {
//創建Person8的匿名子類對象
val p=new Person8("tom")
val g=new GreetDemo
g.greeting(p)
}
}
1.3.9. Scala中抽象類
? 如果在父類中,有某些方法無法立即實現,而需要依賴不同的子類來覆蓋,重寫實現不同的方法。此時,可以將父類中的這些方法編寫成只含有方法簽名,不含方法體的形式,這種形式就叫做抽象方法;
? 一個類中,如果含有一個抽象方法或抽象field,就必須使用abstract將類聲明為抽象類,該類是不可以被實例化的;
? 在子類中覆蓋抽象類的抽象方法時,可以不加override關鍵字;
? 舉例說明:
package cn.itcast.extends_demo
abstract class Person9(val name:String) {
//必須指出返回類型,不然默認返回為Unit
def sayHello:String
def sayBye:String
}
class Student9(name:String) extends Person9(name){
//必須指出返回類型,不然默認
def sayHello: String = "Hello,"+name
def sayBye: String ="Bye,"+name
}
object Student9{
def main(args: Array[String]) {
val s = new Student9("tom")
println(s.sayHello)
println(s.sayBye)
}
}
1.3.10. Scala中抽象field
? 如果在父類中,定義了field,但是沒有給出初始值,則此field為抽象field;
? 舉例說明:
package cn.itcast.extends_demo
abstract class Person10 (val name:String){
//抽象fields
val age:Int
}
class Student10(name: String) extends Person10(name) {
val age: Int = 50
}
1.4. Scala中面向對象編程之trait1.4.1. 將trait作為接口使用
? Scala中的trait是一種特殊的概念;
? 首先先將trait作為接口使用,此時的trait就與Java中的接口 (interface)非常類似;
? 在trait中可以定義抽象方法,就像抽象類中的抽象方法一樣,只要不給出方法的方法體即可;
? 類可以使用extends關鍵字繼承trait,註意,這裏不是 implement,而是extends ,在Scala中沒有 implement 的概念,無論繼承類還是trait,統一都是 extends;
? 類繼承後,必須實現其中的抽象方法,實現時,不需要使用 override 關鍵字;
? Scala不支持對類進行多繼承,但是支持多重繼承 trait,使用 with 關鍵字即可。
? 舉例說明:
package cn.itcast.triat
trait HelloTrait {
def sayHello(): Unit
}
trait MakeFriendsTrait {
def makeFriends(c: Children): Unit
}
//多重繼承 trait
class Children(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable{
def sayHello() =println("Hello, " + this.name)
def makeFriends(c: Children) = println("Hello, my name is " + this.name + ", your name is " + c.name)
}
object Children{
def main(args: Array[String]) {
val c1=new Children("tom")
val c2=new Children("jim")
c1.sayHello()//Hello, tom
c1.makeFriends(c2)//Hello, my name is tom, your name is jim
}
}
1.4.2. 在trait中定義具體的方法
? Scala中的trait不僅可以定義抽象方法,還可以定義具體的方法,此時 trait 更像是包含了通用方法的工具,可以認為trait還包含了類的功能。
? 舉例說明:
package cn.itcast.triat
/**
* 比如 trait 中可以包含很多子類都通用的方法,例如打印日誌或其他工具方法等等。
* spark就使用trait定義了通用的日誌打印方法;
*/
trait Logger {
def log(message: String): Unit = println(message)
}
class PersonForLog(val name: String) extends Logger {
def makeFriends(other: PersonForLog) = {
println("Hello, " + other.name + "! My name is " + this.name + ", I miss you!!")
this.log("makeFriends method is invoked with parameter PersonForLog[name = " + other.name + "]")
}
}
object PersonForLog{
def main(args: Array[String]) {
val p1=new PersonForLog("jack")
val p2=new PersonForLog("rose")
p1.makeFriends(p2)
//Hello, rose! My name is jack, I miss you!!
//makeFriens method is invoked with parameter PersonForLog[name = rose]
}
}
1.4.3. 在trait中定義具體field
? Scala 中的 trait 可以定義具體的 field,此時繼承 trait 的子類就自動獲得了 trait 中定義的 field;
? 但是這種獲取 field 的方式與繼承 class 的是不同的。 如果是繼承 class 獲取的 field ,實際上還是定義在父類中的;而繼承 trait獲取的 field,就直接被添加到子類中了。
? 舉例說明:
package cn.itcast.triat
trait PersonForField {
val age:Int=50
}
//繼承 trait 獲取的field直接被添加到子類中
class StudentForField(val name: String) extends PersonForField {
def sayHello = println("Hi, I‘m " + this.name + ", my age is "+ age)
}
object StudentForField{
def main(args: Array[String]) {
val s=new StudentForField("tom")
s.sayHello
}
}
1.4.4. 在trait中定義抽象field
? Scala中的trait也能定義抽象field, 而trait中的具體方法也能基於抽象field編寫;
? 繼承trait的類,則必須覆蓋抽象field,提供具體的值;
? 舉例說明:
package cn.itcast.triat
trait SayHelloTrait {
val msg:String
def sayHello(name: String) = println(msg + ", " + name)
}
class PersonForAbstractField(val name: String) extends SayHelloTrait {
//必須覆蓋抽象 field
val msg = "Hello"
def makeFriends(other: PersonForAbstractField) = {
this.sayHello(other.name)
println("I‘m " + this.name + ", I want to make friends with you!!")
}
}
object PersonForAbstractField{
def main(args: Array[String]) {
val p1=new PersonForAbstractField("Tom")
val p2=new PersonForAbstractField("Rose")
p1.makeFriends(p2)
}
}
1.4.5. 在實例對象指定混入某個trait
? 可在創建類的對象時,為該對象指定混入某個trait,且只有混入了trait的對象才具有trait中的方法,而其他該類的對象則沒有;
? 在創建對象時,使用 with 關鍵字指定混入某個 trait;
? 舉例說明:
package cn.itcast.triat
trait LoggedTrait {
// 該方法為實現的具體方法
def log(msg: String) = {}
}
trait MyLogger extends LoggedTrait{
// 覆蓋 log() 方法
override def log(msg: String) = println("log: " + msg)
}
class PersonForMixTraitMethod(val name: String) extends LoggedTrait {
def sayHello = {
println("Hi, I‘m " + this.name)
log("sayHello method is invoked!")
}
}
object PersonForMixTraitMethod{
def main(args: Array[String]) {
val tom= new PersonForMixTraitMethod("Tom").sayHello //結果為:Hi, I‘m Tom
// 使用 with 關鍵字,指定混入MyLogger trait
val rose = new PersonForMixTraitMethod("Rose") with MyLogger
rose.sayHello
// 結果為: Hi, I‘m Rose
// 結果為: log: sayHello method is invoked!
}
}
1.4.6. trait 調用鏈
? Scala中支持讓類繼承多個trait後,可依次調用多個trait中的同一個方法,只要讓多個trait中的同一個方法,在最後都依次執行 super 關鍵字即可;
? 類中調用多個trait中都有的這個方法時,首先會從最右邊的trait的方法開始執行,然後依次往左執行,形成一個調用鏈條;
? 這種特性非常強大,其實就是設計模式中責任鏈模式的一種具體實現;
? 案例說明:
package cn.itcast.triat
trait HandlerTrait {
def handle(data: String) = {println("last one")}
}
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check signature: " + data)
super.handle(data)
}
}
class PersonForRespLine(val name: String) extends SignatureValidHandlerTrait with DataValidHandlerTrait {
def sayHello = {
println("Hello, " + this.name)
this.handle(this.name)
}
}
object PersonForRespLine{
def main(args: Array[String]) {
val p=new PersonForRespLine("tom")
p.sayHello
//執行結果:
// Hello, tom
// check data: tom
// check signature: tom
// last one
}
}
1.4.7. 混合使用 trait 的具體方法和抽象方法
? 在 trait 中,可以混合使用具體方法和抽象方法;
? 可以讓具體方法依賴於抽象方法,而抽象方法則可放到繼承 trait的子類中去實現;
? 這種 trait 特性,其實就是設計模式中的模板設計模式的體現;
? 舉例說明:
package cn.itcast.triat
trait ValidTrait {
//抽象方法
def getName: String
//具體方法,具體方法的返回值依賴於抽象方法
def valid: Boolean = {"Tom".equals(this.getName)
}
}
class PersonForValid(val name: String) extends ValidTrait {
def getName: String = this.name
}
object PersonForValid{
def main(args: Array[String]): Unit = {
val person = new PersonForValid("Rose")
println(person.valid)
}
}
1.4.8. trait的構造機制
? 在Scala中,trait也是有構造代碼的,即在trait中,不包含在任何方法中的代碼;
? 繼承了trait的子類,其構造機制如下:
? 父類的構造函數先執行, class 類必須放在最左邊;多個trait從左向右依次執行;構造trait時,先構造父 trait,如果多個trait繼承同一個父trait,則父trait只會構造一次;所有trait構造完畢之後,子類的構造函數最後執行。
? 舉例說明:
package cn.itcast.triat
class Person_One {
println("Person‘s constructor!")
}
trait Logger_One {
println("Logger‘s constructor!")
}
trait MyLogger_One extends Logger_One {
println("MyLogger‘s constructor!")
}
trait TimeLogger_One extends Logger_One {
println("TimeLogger‘s contructor!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println("Student‘s constructor!")
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
//執行結果為:
// Person‘s constructor!
// Logger‘s constructor!
// MyLogger‘s constructor!
// TimeLogger‘s contructor!
// Student‘s constructor!
}
}
[align=left]
1.4.9. trait 繼承 class
? 在Scala中trait 也可以繼承 class,此時這個 class 就會成為所有繼承該 trait 的子類的超級父類。
? 舉例說明:
package cn.itcast.triat
class MyUtil {
def printMsg(msg: String) = println(msg)
}
trait Logger_Two extends MyUtil {
def log(msg: String) = this.printMsg("log: " + msg)
}
class Person_Three(val name: String) extends Logger_Two {
def sayHello {
this.log("Hi, I‘m " + this.name)
this.printMsg("Hello, I‘m " + this.name)
}
}
object Person_Three{
def main(args: Array[String]) {
val p=new Person_Three("Tom")
p.sayHello
//執行結果:
// log: Hi, I‘m Tom
// Hello, I‘m Tom
}
}
spark筆記之類、對象、繼承、特質