Scala的类与Java、C++的类比起来更简洁,学完之后你会更爱Scala!!!
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
}
}