Scala 面向对象编程

来源:互联网 发布:html怎么跟php文件连接 编辑:程序博客网 时间:2024/06/06 02:08

一 定义一个简单的类

1.1定义类,包含field以及方法

classHelloWorld {

  private var name = "leo"

  def sayHello() { print("Hello, " +name) } 

  def getName = name

}                              

1.2创建类的对象,并调用其方法

valhelloWorld = new HelloWorld

helloWorld.sayHello()

print(helloWorld.getName)

 

1.3定义函数括号问题

定义函数我们如果没有参数,可以不加括号,也可以加括号

但是定义的时候,没加,在调用的时候,就不要加,否则会报错;如果在定义的时候加那我们在调用的时候也加上

classHello{

    var name = "nicky"

    def hello(){print("Hello,"+name)}

    def say = print(name+", welcome toyou!")

    def getName = name

}

valhello = new Hello

scala>hello.hello()

Hello,nicky

scala>hello.say

nicky,welcome to you!

scala> hello.say()

<console>:13: error: Unit does not take parameters

       hello.say()

这里就是定义函数没加括号,但是调用的时候加括号了,然后就报错

 

二 字段的get和set方法

2.1定义不带private var field的字段

varname = "icon"

此时,JVM会自动定义为private域,并且提供public的getter和setter方法

classHello {

  var name = "nicky";

  def hello(password:String){

    println("Welcome you to ScalaWorld,"+name+", you password is "+password);

  }

  def say {

     println("Are u "+name+"?")

     println("yes!!")

  }

}

objectTest {

  def main(args:Array[String]):Unit = {

    val hello = new Hello

    hello.hello("123456")

    hello.name = "Claria"

    hello.say

  }

}

运行结果:

Welcomeyou to Scala World,nicky, you password is 123456

Areu Claria ?

yes!!

2.2显示设置private 修饰变量

如果我们显式将字段定义成private,那么此时默认生成的getter和setter方法也是private的,对于其他类不可见。

classHello {

  private var name = "nicky";

  def say {

     println("Are u "+name+"?")

     println("yes!!")

  }

}

objectTest {

  def main(args:Array[String]):Unit = {

    val hello = new Hello

    hello.name ="Claria"//编译通不过

    hello.say

  }

}

2.3字段定义成valfield

如果我们定义字段valfield,那么只会生成get方法,而不会生成set方法,因为  val类型的变量,不能修改其值或者引用

class Hello {

  val name ="nicky";

  def say {

     println("Are u "+name+" ?")

     println("yes!!")

  }

}

objectTest {

  def main(args:Array[String]):Unit = {

    val hello =new Hello

    print(hello.name)//但是可以get

    hello.name = "belly"//编译报错,不能set

    hello.say

  }

}

 

2.4不生成setter和 getter方法

如果希望既不生成setter函数也不希望生成getter函数

那么可以将字段声明为private[this]

class Hello {

  private[this]var name = "nicky";

  def say {

     println("Are u "+name+" ?")

     println("yes!!")

  }

}

 

2.5private var field 和private[this]var field 比较

都只是能在本类使用

privatevar field 可以生成getter和setter方法,但是是私有的

private[this]var field 压根儿就没有get和set方法

言外之意,在同一类,privatevar field 可以set,但是private[this]var field 没有set,不能修改字段

2.6自定义get和set方法

如果只是希望简单的get和set方法,按照scala提供的语法规则就可以了,根据需要为field选择合适的修饰符,var、val、private、private[this]

但是,如果希望更好的控制get和set函数,你可以自定义getter和

Setter方法

class Student  {

  private varsname = "bell"

  //自定义get方法

  def name ="your name is " +sname

 

  //自定义set方法

  def name_=(newValue:String)  {

      sname = newValue

  }

}

注意:

在自定义set方法=号左边不能有空格,否则编译报错

not found: value newValue

 

2.7如果不希望get和set方法暴露,但是希望其他类可以修改某一个字段的值

这时候我们可以将字段定义成private,然后提供一个方法修改该字段

class Student  {

  private varsname = "bell"

  //自定义get方法

  def name ="your name is " +sname

 

  def updateName(sname:String):Unit = {

    this.sname =sname;

  }

}

 

2.8如果不希望当前private 字段被其他类的方法调用时访问,可以使用private[this]

比如,当前类Student,定义一个private字段 sname,如果希望两个实例的name进行比较,private可以实现;但是如果希望不同实例间不能访问该字段,则需要使用private[this]

 

class Student  {

  private varsname = "bell"

 

  def name ="name is "+sname

 

  def name_=(newVal:String){

    sname = newVal

  }

 

  def access(s:Student):Unit = {

    sname.equals(s.sname)

  }

}

 

class Student  {

  private[this]var sname = "bell"

 

  def name ="name is "+sname

 

  def name_=(newVal:String){

    sname = newVal

  }

 

  def access(s:Student):Unit = {

   //value sname is not a member of com.scala.Student

sname.equals(s.sname)

 }

}

 

2.9让Scala自动生成java风格的getter和setter方法

给field添加@BeanProperty注解即可

import scala.beans.BeanProperty

 

class Student  {

  @BeanProperty

  var name:String = _

}

objectTest {

  def main(args:Array[String]):Unit = {

    val s =new Student

    s.setName("nicky")

    print(s.getName)

  }

}

 

 

 

三 constructor 详解

3.1辅助构造器

在scala我们可以定义多个辅助构造函数,类似于Java构造方法重载

辅助构造方法之间可以相互调用,而且第一行必须调用主构造器

class Student  {

  private varname = "";

  private varage = 0;

 

  def this(name:String){

    this();//主构造器

    this.name =name;

  }

 

  def this(name:String,age:Int){

    this(name);

    this.age =age;

  }

}

 

3.2主构造器

Scala中,主constructor是与类名放在一起的,与java不同。而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有java那么清晰

3.2.1普通构造参数

class Student(varname:String,var age:Int)  {

  println("you name is "+name+", age is "+age);

}

我们在初始化的时候,必须提供构造参数,否则报错

objectTest {

  def main(args:Array[String]):Unit = {

    //not enough arguments for constructor Student: (name: String, age: Int)

    //com.scala.Student. Unspecified value parameters name, age.

    val s =new Student

    //提供构造参数就没有问题

    val s1 =new Student("nicky",20)

  }

}

 

3.2.2为构造参数提供默认值

class Student(varname:String ="郭靖",var age:Int = 25)  {

  println("you name is "+name+", age is "+age);

}

objectTest {

  def main(args:Array[String]):Unit = {

    //提供默认的构造参数,就不会报错

    val s =new Student

    //提供构造参数就没有问题

    val s1 =new Student("nicky",20)

  }

}

 

3.2.3构造参数没有生命变量类型和修饰符

如果构造参数没有生命变量类型和修饰符,诸如name:String,如果这样,如果在类里面有方法使用到了,那么该字段就是private[this]修饰;

如果没有方法用到该字段,则表示没有该字段,只是在初始化阶段使用

class Student(name:String,age:Int)  {

  println("you name is "+name+", age is "+age);

}

 

 

四 内部类介绍

Scala也有内部类,但是和Java优点不一样,如果不同的外部类实例的内部类实例是不一样的。

Java实现

import java.util.ArrayList;

import java.util.List;

publicclass Classes {

       class Student {

             private String name;

 

             public Student(String name) {

                    super();

                    this.name =name;

              }

             

       }

       List<Student>students = new ArrayList<Student>();

       public Student getStudent(String name){

             return new Student(name);

       }

      

       public staticvoid main(String[] args) {

              Classesc1 = new Classes();

              Students1 = c1.getStudent("Nicky");

             c1.students.add(s1);

             

              Classesc2 = new Classes();

              Students2 = c2.getStudent("Bell");

             c1.students.add(s2);

       }

}

Scala实现

import scala.collection.mutable.ArrayBuffer

class Class {

  class Student(valname: String) {}

  val students =new ArrayBuffer[Student]

  def getStudent(name:String) =  {

    new Student(name)

  }

  //产生外部类实例班级一

  val c1 =new Class

  //产生一个班级一的学生nicky

  val s1 =c1.getStudent("nicky")

  //把该学生放到班级一的学生集合里面

  c1.students += s1

  //============================================

  //产生外部类实例班级二

  val c2 =new Class

  //产生一个班级二的学生nicky

  val s2 =c2.getStudent("bell")

  //把该学生放到班级一的学生集合里面,这时候会报错

  //因为班级二的学生只能放到班级二的学生集合里

  c1.students += s2

}

 

在Java里只要内部类是同一个类型,都可以加入到某一个班级中,而不用区别Student是哪一个Classes的实例

Scala里,即使内部类是同一个类型,也要区别Student属于的Class实例不一样

 

内部类访问外部类

方式一:外部类名.this.成员

class Classes(val name:String ="全真教") {

  var age:Int = 0;

  def this(age:Int){

    this();

    this.age = age;

  }

 

  class Student(val name: String) {

    def info():Unit = {

      Classes.this.print();

      println("Outer Class Name: "+Classes.this.name+"; Inner Class Name: "+name)

    }

  }

 

  def print(){

    println(this.name+"  "+this.age)

  }

}

方式二 :别名,outrer=>

但是必须放在第一行

class Classes(val name:String ="全真教") {

  outer =>

  class Student(val name: String) {

    def info():Unit = {

      outer.print();

      println("Outer Class Name: "+outer.name+"; Inner Class Name: "+name)

    }

  }

  var age:Int = 0;

  def this(age:Int){

    this();

    this.age = age;

  }

 

  def print(){

    println(this.name+"  "+this.age)

  }

}

 

五 面向对象编程之object

5.1object特性

普通class没有提供一些静态成员变量,不能定义所有实例对象共享的东西;然而这些东西,Scala也可以定义,定义在object里的;

 

第一次调用object的方法时,就会执行object的构造器,而且这个构造器只能在object第一次被调用时执行一次,以后再次调用不会执行了

object通常用于作为单例模式的实现,或者放class的静态成员,比如一些工具方法

objectPerson{

      private var eyeNum:Int = 2

      print("this is Person object,constructor is exexuting")

      def getEyeNum = eyeNum

}

scala>Person.getEyeNum

thisis Person object, constructor is exexutingres4: Int = 2

 

scala>Person.getEyeNum

res5:Int = 2

 

5.2伴生对象和伴生类

如果有一个class,还有一个跟class同名的object,那么简称这个object是class的伴生对象;class是object的伴生类

伴生类和伴生对象必须同在一个scala文件

伴生类和伴生对象可以互相访问private字段

/**

 *作为Person object的伴生类

 */

class Person {

  var name:String =""

  var age:Int =0

  private varaddress:String =""

  private[this]var hobby:String = ""

 

  def this(name:String,age:Int,address:String,hobby:String){

    this()

    this.name =name

    this.age =age

    this.address =address

    this.hobby =hobby

  }

  def output =println("Name -->"+name+" Age -->"+age+" address -->"

      +address+" hobby -->"+hobby+" EyeNum -->"+Person.eyeNum)

}

 

/**

 *作为class person的伴生对象

 */

objectPerson{

  private vareyeNum:Int = 2;

  println("这是Person object,构造器开始执行")

  def getEyeNum =eyeNum

}

 

5.3object继承

object也可以继承抽象类,并覆盖抽象类中的方法

abstractclass Hello(varmessage:String) {

  def sayHello(name:String):Unit{}

}

 

objectHelloImpl extends Hello("Welcome you"){

  override defsayHello(name:String):Unit = {

    println(message+", "+name);

  }

}

 

5.4apply方法

>>apply方法是一个object非常特殊的方法,通常在伴生对象object中实现该方法,并在其中实现构造伴生类的的对象的功能.

 

>>创建伴生类对象时,通常不会使用new 类名方式,而使用类名()的方式,隐式调用伴生对象的apply方法这样会让创建对象更加简洁.

 

Array类的伴生对象就实现了接收可变数量参数,并创建一个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

}

 

定义自己的伴生类和伴生对象

class Human(valname:String,val age:Int){

  println("初始化Human class")

  def info(){

    println(this.name+" "+this.age)

  }

}

 

objectHuman {

  def say():Unit = {

    println("Human can speak");

  }

 

  def apply(name:String,age:Int):Human = {

    new Human(name,age)

  }

 

  def main(args:Array[String]):Unit = {

    val human =Human("郭靖",25)

    Human.say()

    human.info()

  }

}

 

5.5main方法

我们需要一个执行代码的入口类,在Scala里面,我们也需要定义一个main方法,而且这个main方法只能在object里面

defmain(args:Array[String]):Unit = {

    val human =Human("郭靖",25)

    Human.say()

    human.info()

  }

 

除了main方法以外,我们还可以集成App Trait,然后将需要在main方法执行的代码直接作为object的构造器代码,而且用args可以接收传入的参数

object HelloWorldextends App {

       if (args.length > 0) {

              print("arguments list: ")

             for(i <- 0 until args.length ) {

                     print(args(i) +" ")

              }

       }else{

              print("No any arguments passed!")

       }

}

AppTrait的工作原理:

App继承自DelayedInitTrait,scalac 命令进行编译时,会把AppTrait的object的构造器代码放到DelayedInitTrait的delayedInit方法执行

 

5.6object实现枚举功能

Scala没有直接提供类似于Java的枚举功能,如果Scala要实现枚举,需要extendsEnumeration类,并且调用value方法来初始化枚举值

objectSeason extendsEnumeration {

  val SPRING,SUMMER, AUTUMN,WINTER = Value

}

 

还可以通过Value传入枚举值的idname,通过idtoString可以获取;还可以通过idname来查找枚举值

 

objectSeason extends Enumeration {

    val SPRING =Value(0,"spring")

    val SUMMER =Value(1,"summer")

    val AUTUMN =Value(2,"autumn")

    val WINTER =Value(3,"winter")

   

    def main(args:Array[String]){

      for (i <-Season values) {

       println(i)

      }

    }

}

 

5.7 object 与 class区别

>>关键字不一样

>>object不能接受带参数的构造器;class 可以接收带参数的构造器

>>object可以定义一些类似静态的成员变量,然后所有实例共享;class不能定义静态成员变量

>>object可以定义apply方法实例化class

>>class可以直接访问

 

六 面向对象之继承

6.1extends

Scala的继承和Java基本差不多,都是用extends关键字;

父类如果是final,则不能被继承;

父类方法或者字段是final,则不能被子类覆盖

 

6.2override & super

 

覆盖父类方法或者var|val字段。使用关键字override

调用父类的方法或者字段等使用关键字super

而且子类的valfield还可以覆盖父类的valfield的getter方法;只要在子类中使用override关键字即可

class Person {

  private var name = "leo"

  def getName = name

}

class Student extends Person {

  private var score = "A"

  def getScore = score

  override def getName = "Hi, I'm " + super.getName

}

 

6.3isInstanceOf 和 asInstanceOf

isInstanceOf[类名]:判断是否是某一个类的实例;

 

asInstanceOf[类名]:将某一个类的实例强制转换

Java实现

publicclass Person {

       Stringname = null;

       public Person(String name) {

 

             this.name =name;

       }

       public String getName() {

             return name;

       }

       public void setName(Stringname) {

             this.name =name;

       }

}

publicclass Student extends Person{

       private doublescore;

       public Student(String name,double score) {

             super(name);

             this.score =score;

       }

       public void outPut() {

              System.err.println(this.name+" "+this.score);

       }

       public double getScore() {

             return score;

       }

       public void setScore(doublescore) {

             this.score =score;

       }

}

       public staticvoid main(String[] args) {

              Personp = new Student("洪七公", 95.5);

              Students = null;

             if (pinstanceof Student) {

                    s = (Student) p;

                    s.outPut();

              }

       }

 

Scala实现:

class Person(varname:String,var age:Int) {

  println("Person 主构造器开始工作!")

  val skin:String =null;

  def say:Unit =print("说人话");

}

class Student(name:String,age:Int,var score:Double) extends Person(name,age){

  println("Student 主构造器开始工作!")

  var classes:String =null;

  def this(name:String,age:Int,score:Double,classes:String){

    this(name,age,score);

    this.classes =classes;

    println("Student 辅助构造器开始工作!");

  }

  override valskin:String ="黄色"

  override defsay:Unit = println("讲汉语");

}

objectTest {

  def main(args: Array[String]): Unit = {

    val p:Person =new Student("西门吹雪",25,85,"剑客班");

    var s:Student =null;

    if (p.isInstanceOf[Student]) {

      s = p.asInstanceOf[Student];

    }

  }

}

 

注意:

val p:Person = new Student("西门吹雪",25,85,"剑客班");

p.isInstanceOf[Person] = tue ?

其实过程是:

它首先判断是不是Person的实例,不是,然后继续判断是不是Person子类的实例,如果还不是返回false,如果是返回true.

==>所以这里的判断类型不算是很精确的

 

6.4getClass 和 classOf

我们知道:isInstanceOf只能判断出对象是否是指定类以及其子类的对象,而不能精确判断出,对象就是指定类的对象。

 

如果要求精确地判断对象就是指定类的对象,那么就只能使用getClass和classOf了

 

对象.getClass可以精确获取对象的类,classOf[类]可以精确获取类,然后使用==操作符即可判断

scala>class Person

definedclass Person

 

scala>class Student extends Person

definedclass Student

 

scala>val p:Person = new Student

p:Person = Student@48d5f34e

 

scala>p.isInstanceOf[Person]

res3:Boolean = true

 

scala>p.isInstanceOf[Student]

res4:Boolean = true

 

scala>p.getClass == classOf[Person]

res5:Boolean = false

 

scala>p.getClass == classOf[Student]

res6:Boolean = true

6.5使用模式匹配进行类型判断

使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断主要是该类以及该类的子类的对象即可,不是精准判断的

scala>p match {

     | case per:Person => println("It'sa Person Object")

     | case _ => println("UnknownType!!")

     | }

It'sa Person Object

6.6protected

Scala也可以使用protected关键字来修饰字段和方法,protected修饰的字段和方法在子类是不需要通过super去调用的,直接就可以访问

protected[this]:只能在当前子类对象中访问父类的字段和方法,无法通过其他子类对象访问父类的字段和方法

class Human {

  protected varname: String ="nicky"

  protected[this]var hobby: String = "game"

}

class Workerextends Human {

  def sayHello =println("Hello, "+ name)

  def makeFriends(s: Student) {

    //s.hobby 就会编译报错,因为不是当前子类,而是父类的其他子类

    println("my hobby is " + hobby + ", your hobby is " + s.hobby)

  }

}

 

6.7调用父类的构造器

Scala中,每个类可以有一个主constructor和任意多个辅助constructor,而每个辅助constructor的第一行都必须是调用其他辅助constructor或者是主constructor;因此子类的辅助constructor是一定不可能直接调用父类的constructor

 

只能在子类的主constructor中调用父类的constructor,以下这种语法,就是通过子类的主构造函数来调用父类的构造函数

 

注意!如果是父类中接收的参数,比如nameage,子类中接收时,就不要用任何valvar来修饰了,否则会认为是子类要覆盖父类的field,举个例子:

class Human(varname:String,var age:Int) {

  def outPut:Unit = {

    println(this.name+ "<<>>" + this.age)

  }

}

/*

 * var name:String, var age:Int编译报错

 *认为是子类要覆盖父类的nameage属性

 */

class Worker(varname:String,var age:Int)extends Human(name,age) {

 

}

 

如果我把变量名改一下:

Woker(varname1:String,var age1:Int) extends Human(name1,age1) {

 

}

这样就不会报错,的确,确实可以如此,但是,我们却增加了2个字段,浪费空间

标准写法:

class Worker(name:String,age:Int,var score:Double) extends Human(name,age) {

  def this(name:String) {

    this(name,0, 0)

  }

 

  def this(name:String,age: Int) {

    this(name,age, 0)

  }

}

 

另外,自定义辅助的构造函数参数类型不能和主构造函数相同,否则认为重复了,会编译报错

 

6.8匿名内部类

可以定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量。这个就是匿名内部类,我们甚至可以将该匿名内部类对象传递给其他函数

 

匿名内部类在java实现:

new父类构造器(参数列表)|实现接口()    {   

     //匿名内部类的类体部分   

}

publicabstract class Bird {

       private String name;

 

       public String getName() {

             return name;

       }

 

       public void setName(Stringname) {

             this.name =name;

       }

 

       public abstractint fly();

}

publicinterface Animal {

       public abstractvoid feed();

       public abstractvoid getType();

}

             new Bird() {

                    @Override

                    public int fly() {

                           return 1000;

                     }

              };

              Animalanimal = new Animal() {

                    @Override

                    public void getType() {

                            System.out.println("飞禽");

                     }

                    @Override

                    public void feed() {

                           System.out.println("觅食");

                     }

              };

}

 

Scala实现

class Human(varname:String,var age:Int) {

  def outPut:Unit = {

    println(this.name+ "<<>>" + this.age)

  }

}

objectTest {

  def main(args: Array[String]): Unit = {

    val h:Human =new Human("杨过",20){

      override defoutPut:Unit = {

       println(this.name+ "#####" + this.age)

      }

    }

    h.outPut

  }

}

 

6.9抽象类和抽象方法

在Scala抽象的定义和java一样:

>>如果在父类中,有某些方法无法立即实现,而需要依赖不同的子来来覆盖,重写实现自己不同的方法实现。此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法。

>>而一个类中如果有一个抽象方法,那么类就必须用abstract来声明为抽象类,此时抽象类是不可以实例化的

>>但是,在子类中覆盖抽象类的抽象方法时,不需要使用override关键字

>>如果父类子段没有初始化值,那么父类必须是抽象的,而且子类需要重写这个属性,但是不用使用关键字override,否则子类编译会报错

abstractclass Animal(name:String) {

var age:Int

  def eat:Unit

  def sleep(){

    println("sleep 8 hours")

  }

}

class Lion(name:String)extends Animal(name){

var age:Int = 0

  def eat:Unit = {

    println("狮子吃肉");

  }

}

objectTest {

  def main(args: Array[String]): Unit = {

    val lion =new Lion("美洲狮");

    lion.eat

    lion.sleep()

  }

}

 

 

七 面向对象编程之Trait

7.1Trait基础知识

7.1.1trait作为接口使用

我们可以将trait作为接口使用,这时候和Java的接口非常类似

特性:

>>trait中可以定义抽象方法,和抽象类定义抽象方法一样,不给出具体实现即可

>>类可以使用extends集成trait,这里和Java有所区别,Scala没有实现的概念,无论是继承类还是trait都用extends

>>类继承之后,必须实现其中的抽象方法,实现不需要override

>>Scala不允许类多继承,但是trait可以多继承,使用with关键字即可

traitHelloTrait {

  def sayHello(name:String)

}

traitMakeFriendsTrait  {

  def makeFriends(p: Person)

}

import com.scala.traits.HelloTrait

import com.scala.traits.MakeFriendsTrait

 

class Person(valname:String)extends HelloTrait with MakeFriendsTrait with Serializable {

  def sayHello(name:String):Unit = {

    println("Hello, "+name)

  }

  def makeFriends(p: Person){

    println("你好,我是"+name+",您的名字是"+p.name+"?")

  }

}

objectTest {

  def main(args: Array[String]): Unit = {

    val p1:Person =new Person("张无忌")

    p1.sayHello("周芷若")

    val p2:Person =new Person("赵敏")

    p1.makeFriends(p2)

  }

}

Hello, 周芷若

你好,我是张无忌, 您的名字是赵敏吗?

 

7.1.2trait中定义具体方法

trait中不仅只是可以定义抽象方法,还可以定义具体方法,就像一个包含通用工具方法的东西;相当于时trait混入了类

traitLogger  {

  def log(message:String) = println(message)

}

class Person(valname:String)extends HelloTrait with MakeFriendsTrait with Logger with Serializable {

  def sayHello(name:String):Unit = {

    println("Hello, "+name)

  }

 

  def makeFriends(p: Person){

    println("你好,我是"+name+",您的名字是"+p.name+"?")

    log(name+"正在和"+p.name+"交朋友")

  }

}

 

7.1.3trait中定义具体字段

Scala中的trait可以定义具体field,此时继承trait的类就自动获得了trait中定义的field;

但是这种获取field的方式与继承class是不同的:如果是继承class获取的field,实际是定义在父类中的;而继承trait获取的field,就直接被添加到了类中

traitLogger  {

  val logLevel:String ="info";

  def log(message:String) = println(message)

}

class Person(valname:String)extends HelloTrait with MakeFriendsTrait with Logger with Serializable {

  def sayHello(name:String):Unit = {

    println("Hello, "+name)

  }

 

  def makeFriends(p: Person){

    println("你好,我是"+name+",您的名字是"+p.name+"?")

    log("日志级别: "+p.logLevel)

    log(name+"正在和"+p.name+"交朋友")

  }

}

 

7.1.4trait中定义抽象字段

trait可以定义抽象方法,继承trait的类,则必须覆盖抽象字段;但是继承trait的trait,可以不必实现;可以理解为抽象类继承抽象类,然后只有继承抽象类的类才会实现具体的东西

trait SayHello {

  val msg: String

  def sayHello(name: String) = println(msg + ", " + name)

}

class Person(val name: String) extends SayHello {

  val msg: String = "hello"

  def makeFriends(p: Person) {

    sayHello(p.name)

    println("I'm " + name + ", I want to make friends with you!")

  }

}

 

7.2Trait高级知识

7.2.1为实例对象混入trait

traitLogged {

  def log(msg:String) {}

}

traitMyLogger extendsLogged {

  override deflog(msg: String) { println("log: "+ msg) }

}

class Person(valname: String)extends Logged {

  def sayHello {

    println("Hi, I'm " + name)

    log("sayHello is invoked!")

  }

}

objectTest {

  def main(args: Array[String]): Unit = {

    val p =new Person("金毛狮王")with MyLogger

    p.sayHello

  }

}

 

7.2.2trait调用链

>>首先,有一个parent trait,然后类要继承的所有trait都来继承parent trait

 

>>每一个trait实现父trait或者重写父trait 需要处于调用链上的方法,每一个方法调用完后,都调用一下super.方法名

 

>>类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条

 

>>这种特性非常强大,其实就相当于设计模式中的责任链模式的一种具体实现依赖

traitHandler {

  def handle(data:String) {}

}

traitDataValidHandler extendsHandler {

  override defhandle(data:String) {

    println("数据校验: "+data);

    super.handle(data)

  }

}

traitSignatureValidHandler extendsHandler {

  override defhandle(data:String) {

    println("检查签名: "+ data)

    super.handle(data)

  }

}

 

7.2.3trait中覆盖抽象方法(有何意义)

>>在trait中,是可以覆盖父trait的抽象方法的

>>但是覆盖时,如果使用了super.方法的代码,则无法通过编译。因为super.方法就会去掉用父trait的抽象方法,此时子trait的该方法还是会被认为是抽象的

>>如果要通过编译,就得给子trait的方法加上abstractoverride修饰

trait Logger {

  def log(msg: String)

}

trait MyLogger extends Logger {

  abstract override def log(msg: String) { super.log(msg) }

}

 

 

7.2.4混合使用trait的具体方法和抽象方法

>>在trait中,可以混合使用具体方法和抽象方法

>>可以让具体方法依赖于抽象方法,而抽象方法则放到继承trait的类中去实现

>>这种trait其实就是设计模式中的模板设计模式的体现

Java实现:

publicabstract class CaffeineBeverage { 

   

    /**

     * 

     *@desc 

     *         模板方法,用来控制泡茶与冲咖啡的流程

     *         申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序

     *@return void

     */ 

    final voidprepareRecipe(){ 

        boilWater(); 

        brew(); 

        pourInCup(); 

        addCondiments(); 

    } 

     

    /**

     *@desc 

     *      brew()addCondiment()声明为抽象类,具体操作由子类实现

     *@return void

     */ 

    abstract void brew(); 

     

    abstract void addCondiments(); 

     

    void boilWater(){ 

        System.out.println("Boiling water..."); 

    } 

     

    void pourInCup(){ 

        System.out.println("Pouring into Cup..."); 

    } 

}

publicclass Coffee extends CaffeineBeverage{ 

        

    void addCondiments() { 

        System.out.println("Adding Sugar and Milk..."); 

    } 

 

    void brew() { 

        System.out.println("Dripping Coffee through filter..."); 

}  

 

Scala实现:

traitCook {

  def brew

  def addCondiments

  def boilWater(){

    println("Boiling water...")

  }

  def pourInCup(){

     println("Pouring into Cup...")

  }

 

  def prepareRecipe():Unit = {

    boilWater()

    brew

    pourInCup()

    addCondiments

  }

}

import com.scala.traits.Cook

 

class Teaextends Cook{

    def addCondiments(){ 

      println("Adding Sugar and Milk..."); 

    } 

 

    def brew(){ 

      println("Dripping tea through filter..."); 

    } 

}

 

7.2.5trait的构造机制

trait也是有构造代码的概念,我们解读以下整个流程:

>>如果有父类,先构造父类

>>如果父类继承trait,先构造trait

>>如果trait有多个,从左右到右依顺序构造

>>如果trait还有父trait,那么父trait先构造

>>如果多个trait继承自同一个trait,这个trait只会被构造一次

traitHandler {

  println("Handler Trait 构造函数执行...");

  def handle(data:String) {}

}

traitDataValidHandler extendsHandler {

  println("DataValidHandler Trait 构造函数执行...");

  override defhandle(data:String) {

    println("数据校验: "+data);

    super.handle(data)

  }

}

traitSignatureValidHandler extendsHandler {

  println("SignatureValidHandler Trait 构造函数执行...");

  override defhandle(data:String) {

    println("检查签名: "+ data)

    super.handle(data)

  }

}

class Person(valname: String)extends SignatureValidHandler withDataValidHandler {

  println("Person 类主构造函数执行...")

  def sayHello = {println("Hello, "+ name); handle(name) }

}

class Student(name:String) extends Person(name){

  println("Student 主构造函数执行...");

}

objectTest {

  def main(args: Array[String]): Unit = {

    val s:Student =new Student("林晚荣")

  }

}

运行结果:

Handler Trait 构造函数执行...

SignatureValidHandler Trait 构造函数执行...

DataValidHandler Trait 构造函数执行...

Person 类主构造函数执行...

Student 主构造函数执行...

 

7.2.6trait字段的初始化

默认情况下,Scala中,trait是没有接收参数的构造函数的,这是trait与class的唯一区别,但是如果需求就是要trait能够对field进行初始化,该怎么办呢?只能使用Scala中非常特殊的一种高级特性——提前定义

下面代码会报空指针异常:因为trait先于子类构造,但是trait这会儿msg还没有初始化呢

traitSayHello {

  val msg:String

  println(msg.toString)

}

class Personextends SayHello{

  val msg:String ="init";

}

objectTest {

  def main(args: Array[String]): Unit = {

    val p =new Person

  }

}

 

方式一:

class Personextends {

  val msg:String ="init";

} withSayHello

 

方式二:使用lazyvalue

traitSayHello {

  lazy valmsg: String = null

  println(msg.toString)

}

class Personextends SayHello{

  override lazyval msg:String ="init";

}

objectTest {

  def main(args: Array[String]): Unit = {

    val p =new Person

  }

}

 

方式三:

val p =new {

  val msg: String = "init"

} with Personwith SayHello

 

7.2.7让trait继承类

在Scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类

class MyUtil {

  def printMessage(msg:String) = println(msg)

}

 

traitLogger extends MyUtil {

  def log(msg:String) = printMessage("log: " + msg)

}

 

class Person(valname: String)extends Logger {

  def sayHello {

    log("Hi, I'm " + name)

    printMessage("Hi, I'm " + name)

  }

}

 

0 0
原创粉丝点击