Scala學習教程筆記二之函數式編程、Object對象、伴生對象、繼承、Trait、


1:Scala之函數式編程學習筆記:

1:Scala函數式編程學習:
    1.1:Scala定義一個簡單的類,包含field以及方法,創建類的對象,並且調用其方法:
        class User {
            
          private var name = "張三";
          def hello(): Unit ={
            println("hello : " + name)
          }
          //注:如果定義方法時不帶括號,則調用的時候也不可以加括號,否則報錯。
          def getName = name;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {
            
          def main(args: Array[String]): Unit = {
            var user = new User;
            user.hello();
            println(user.getName)
          }
        }
2:Scala中field字段的getter和setter詳解教程:
    2.1:定義不帶private的var field,此時scala生成的面向jvm的類時,會定義為Private的name字段,並提供public的getter和setter的方法:
        class User {

          var name = "張三";
          var age = 15;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            user.name="李四";
            user.age=20;
            println(user.name + " " + user.age)
          }
        }

    2.2:如果使用private修飾field,則生成的getter和setter也是private的:
        class User {

          private var name = "張三";
          private var age = 15;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            //如果使用private修飾field,則生成的getter和setter也是private的,所以調用會報錯。
            //println(user.name + " " + user.age)
          }
        }
    2.3:如果定義val field,則只會生成getter方法:
        class User {

          val name = "張三";
          val age = 15;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            //如果定義val field,則只會生成getter方法。所以調用setter方法會報錯的。
            //user.name_="張三";
            println(user.name + " " + user.age)
          }
        }
    2.4:如果不希望生成setter和getter方法,則將field聲明為private[this]:
        class User {

          private[this] var name = "張三";
          private[this] var age = 15;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            //則將field聲明為private[this],不生成setter和getter方法。所以調用setter和getter方法報錯
            //user.name_="張三";
            //println(user.name + " " + user.age)
          }
        }
    2.5:調用getter和setter方法,分別叫做name和name_=
        class User {

          var name = "張三";
          var age = 15;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            println(user.name + " " + user.age)
            //調用setter方法來修改值
            user.name="李四";
            println(user.name + " " + user.age)
          }
        }
    2.6:如果只是希望擁有簡單的getter和setter方法,那么就按照scala提供的語法規則,根據需求為field選擇合適的修飾符就好:var,val,private,private[this];
        注意:如果希望能夠自己對gettter和setter進行控制,則可以自定義getter和setter方法,自定義setter方法的時候一定要注意scala的語法限制,簽名,=,參數間不能有空格
        class User {
          var name : String = "張三";
          var age : Int = 15;
          def getName = "my name is : " + name;
          def setName_=(newName : String): Unit ={
            println("不可以修改你的姓名。")
          }
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            user.name="李思思"
            println(user.getName + " " + user.age)

          }
        }
    2.7:如果不希望field有setter方法,則可以定義為val,但是此時就再也不能更改field的值了。如果希望能夠僅僅暴漏出一個getter方法,並且還能通過某些方法更改field的值,那么需要綜合使用private以及自定義getter方法。此時,由於field是private的,所以setter和getter都是private的,對外界沒有暴漏,自己可以實現修改field值的方法;自己可以覆蓋getter方法。
        class User {

          private var myName : String = "張三";
          def updateName(newName : String): Unit ={
            if(newName == "李四"){
              myName = newName;
            }else{
              println("此值不可以修改")
            }
          }

          def name = "you name is :" + myName;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            user.updateName("李四");
            println(user.name)

          }
        }
    2.8:如果將field使用private來修飾,那么代表這個field是類私有的,在類的方法中,可以直接訪問類的其他對象的private field;這種情況下,如果不希望field被其他對象訪問到,那么可以使用private[this],意味着對象私有的field,只有本對象內可以訪問到。
        class User {

         private[this] var myAge : Int = 0;
         def age_=(newAge : Int): Unit ={
           if(newAge > 0){
              myAge = newAge;
           }else println("不合法的年齡")
         }

          def age = myAge;
          //使用private[this],意味着對象私有的field,只有本對象內可以訪問到。
          //def orderAge(user : User) ={
            //myAge > user.myAge;
          //}
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            user.age_=(20);
            println(user.age)
            user.age = 24;
            println(user.age)
            var user2 = new User;
            user2.age_=(25);
            if(user2.orderAge(user)){
              println("user2 大於 user")
            }else{
              println("user2 小於 user")
            }
          }
        }
    2.9:Scala的getter和setter方法的命名與java是不同的,是field和field_=的方式,如果要讓scala自動生成java風格的getter和setter方法,只要給field添加@BeanProperty注解即可;此時會生成4個方法,name:String,name_=(newName:String):Unit,getName():String,setName_(newValue:String):Unit;
        class User {

          @BeanProperty var name : String = _;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            user.setName("張三");
            println(user.getName);
            user.name_=("李思思");
            println(user.name)
          }
        }        
3:Scala中constructor詳解:
    3.1:Scala中,可以給類定義多個輔助constructor,類似於java中的構造函數重載;輔助constructor之間可以互相調用,而且必須第一行調用主constructor
        class User {

          @BeanProperty var name : String = _;
          @BeanProperty var age : Int =_;

          def this(name: String){
            this();
            this.name = name;
          }
          def this(name : String, age :Int){
            this(name);
           this.age = age;
          }
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User;
            var user2 = new User("張三");
            var user3 = new User("李四",26);
          }
        }
    3.2:Scala中,主構造constructor是與類名放到一起的,與java不同。而且類中,沒有定義在任何方法或者是代碼塊之中的代碼,就是主constructor的代碼,這是感覺沒有java那么清晰;
        注意:如果主constructor傳入的參數什么修飾都沒有,比如name:String。那么類內部的方法使用到了,則會聲明為private[this] name,否則沒有該field,就只能被constructor代碼使用而已。
        class User(name : String, age : Int) {
          println("you name is :" + name + ", you age is : "+ age)

          var id : Int = _;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User("張三",16);
            user.id_=(20);
            println("you id is :" +user.id)

          }
        }
    3.3:主構造方法constructor方法中還可以通過使用默認參數,來給參數默認的值:
        class User(val name : String="李思思", age : Int = 18) {
          println("you name is :" + name + ", you age is : "+ age)

          var id : Int = _;
        }
        //創建一個object來調用練習的實體類對象.
        object Hello {

          def main(args: Array[String]): Unit = {
            var user = new User();
            user.id_=(20);
            println("you id is :" +user.id)

          }
        }
4:Scala中內部類的介紹:
    4.1:在Scala中,同樣可以在類中定義內部類,但是與java不同的是,每個外部類的對象的內部類,都是不同的類:
        class User {
                
          class Student(val name : String){};
          val students = new ArrayBuffer[Student];
          def getStudent(name : String) = {
            new Student(name);
          }
        }
        //創建一個object來調用練習的實體類對象.
            object Hello {

              def main(args: Array[String]): Unit = {
                val user1 = new User();
                val stu = user1.getStudent("張三");
                println(stu)
                user1.students += stu;

                var user2 = new User;
                var stu2 = user2.getStudent("李四");
                println(stu2)
                //下面這一行報錯,好好體會一下
                //user1.students += stu2;
              }
            }

2:Scala之Object對象學習筆記:

1Object對象:
    1.1:object對象,相當於class的單個實例,通常在里面放一些靜態的field或者method;第一次調用object的方法時候,就會執行object的constructor構造方法,也就是Object內部不在method中的代碼;但是Object不能定義接受參數的constructor;object通常用於作為單例模式的實現,或者放class的靜態成員,比如工具方法;
        注意:object的constructor只會在其第一次被調用的時候執行一次,以后再次調用就不會執行constructor了。
        object Hello {

          private val name = "張三";
          println("this is object Hello");
          def show(): Unit ={
            println("一步一個腳印");
          }
          def getName = name;
          def main(args: Array[String]): Unit = {
            show()
            println(getName);
            println(Hello.getName)
          }
        }

3:Scala之伴生對象學習筆記:

1:伴生對象,如果有一個class,還有一個與class同名的Object,那么就稱這個object是class的伴生對象,class是object的伴生類;伴生類與伴生對象必須存放在一個.scala文件之中;伴生類與伴生對象,最大的特點就是在於,互相可以訪問private field;

4:Scala之繼承學習筆記:

1:讓object繼承抽象類:
    1.1:object的功能其實和class類似,除了不能定義接受參數的constructor之外,object也可以繼承抽象類,並且覆蓋抽象類中的方法:
        abstract class User(val name : String) {

          def hello(name : String): Unit ={
            //println("you name is : "+ name)
          }
        }
        //創建一個Object繼承User類
        object UserImpl extends User("張三"){

          override def hello (name: String): Unit = {
            println("you name is :" + name)
          }
        }
        //創建一個Object來進行測試
        object Test {

          def main(args: Array[String]): Unit = {
            //object的功能其實和class類似,除了不能定義接受參數的constructor之外
            var ui = UserImpl;
            //方式一
            ui.hello("李四");
            //方式二
            UserImpl.hello("王五");
          }
        }
2:Apply方法,object中非常重要的一個特殊方法,就是apply方法。通常在伴生對象中實現apply方法,並在其中實現構造伴生類的對象的功能。而創建伴生類的對象時,通常不會使用new Class的方式,而是使用Class()的方式,隱式的調用伴生對象得到apply方法,這樣會讓對象創建更加簡潔:
    2.1:比如,Array類的伴生對象的apply方法就實現了接受可變數量的參數,並且創建一個Array對象的功能:
        var arr = Array(1,2,3,4,5,6,7,8,9);
    2.2:比如,定義自己的伴生類和伴生對象:    
        class Person(val name : String) {
      
        }    
        //創建伴生對象
        object Person{
            def apply(name : String) = new Person(name)
        }
        //創建Object進行測試
        object Test {

          def main(args: Array[String]): Unit = {
            val p1 = new Person("張三");
            println(p1.name);
            val p2 = Person("李思思");
            println(p2.name);
          }
        }
3:main方法,就如同java中,如果要運行一個程序,必須編寫一個包含main方法類一樣,在scala中,如果想要運行一個應用程序,那么必須有一個main方法,作為入口;
    3.1:注意:scala中的main方法定義為def main(args: Array[String]): Unit = {}。而且必須定義在object中;
        App Trait的工作原理,App Trait繼承自DelayedInit Trait,scalac命令進行編譯時候,會把繼承App Trait的object的construtcor代碼都放到DelayedInit Trait的delayedInit方法中執行;
        
        object Test {

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

          }
        }        
    3.2:除了自己實現main方法以外,還可以繼承App Trait,然后將需要在main方法中運行的代碼,直接作為Object的construstor代碼。而且用args可以接受傳入的參數:
        object Test extends App{

          if(args.length > 0){
            println("hello : " + args(0))
          }else println("hello 你妹啊 hello.")
        }    
4:用object來實現枚舉功能:
    4.1:scala沒有直接提供類似於java中的Enum這樣的枚舉特性,如果要實現枚舉,則需要用Object繼承Enumeration類,並且調用value方法來初始化枚舉值:
        object Season extends Enumeration{

          val SPRING,SUMMER,AUTUMN,WINTER = Value;
        }
    4.2:還可以通過value傳入枚舉值的id和name,通過id和toString可以獲取,還可以通過id和name來查找枚舉值:
        object Season extends Enumeration{

        //  val SPRING,SUMMER,AUTUMN,WINTER = Value;
          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]): Unit = {
            println(Season(0));
            println(Season.withName("spring"));
          }
        }
    4.3:使用枚舉object.values可以遍歷枚舉值:
        object Season extends Enumeration{

          //val SPRING,SUMMER,AUTUMN,WINTER = Value;
          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]): Unit = {
            for(i <- Season.values){
              println(i)
            }
          }
        }
5:Scala中,讓子類繼承父類,與Java一樣,也是使用extends關鍵字;
    5.1:繼承就代表,子類可以從父類繼承父類的field和method,然后子類可以在自己內部放入父類沒有的field或者method;子類擁有特有的field和method,使用繼承可以有效的復用代碼。
        class Person {

          private var name : String = "張三";
          def getName = name;
          def setName_=(name : String): Unit ={
            println("private修改的字段,生成的setter和getter也是私有的。")
          }
        }
        //創建一個Student類來繼承Person類:
        class Student extends Person{

          private  var score : Int = 60;
          def getScore = score;
          def setScore_=(score : Int): Unit ={
            println("private修改的字段,生成的setter和getter也是私有的。")
          }
        }
        //創建一個Object來調用創建的Student或者Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var student = new Student();
            println("my name is : " + student.getName + ", and my score is : " + student.getScore);
          }
        }
    5.2:子類可以覆蓋父類的field和method,但是如果父類用final修飾,field和method用final修飾,則該類是無法被繼承的,field和method是無法被覆蓋的。
        //父類用final修飾
        final class Person {

          private var name : String = "張三";
          def getName = name;
          def setName_=(name : String): Unit ={
            println("private修改的字段,生成的setter和getter也是私有的。")
          }
        }
        //創建一個Student類來繼承Person類:
        class Student extends Person{

              private  var score : Int = 60;
              def getScore = score;
              def setScore_=(score : Int): Unit ={
                println("private修改的字段,生成的setter和getter也是私有的。")
              }
            }
        //創建一個Object來調用創建的Student或者Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var student = new Student();
            //父類用final修飾,則該類是無法被繼承的,所以下面會報錯
            //println("my name is : " + student.getName + ", and my score is : " + student.getScore);
          }
        }
6:Scala中的override和super:
    6.1:Scala中,如果子類要覆蓋一個父類中的非抽象方法,則必須使用override關鍵字;override關鍵字可以幫助我們盡早的發現代碼里面的錯誤,比如,override修改的父類方法的方法名我們拼寫錯誤了,比如要覆蓋的父類方法的參數我們寫錯了等等。此外,在子類覆蓋父類方法以后,如果我們在子類中就要調用父類的被覆蓋的方法呢?那就可以使用super關鍵字,現實的指定要調用父類的方法。
        class Person {

          private var name : String = "張三";
          def getName = name;
          def setName_=(name : String): Unit ={
            println("private修改的字段,生成的setter和getter也是私有的。")
          }
        }
        //創建一個Student類來繼承Person類:
        class Student extends Person{

              private  var score : Int = 60;
              def getScore = score;
              def setScore_=(score : Int): Unit ={
                println("private修改的字段,生成的setter和getter也是私有的。")
              }
              //方法的覆蓋,使用關鍵詞override和super
              override def getName: String = "Student類繼承Person,且覆蓋getName方法:" + super.getName;
            }
        //創建一個Object來調用創建的Student或者Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var student = new Student();
            println("my name is : " + student.getName + ", and my score is : " + student.getScore);
          }
        }
    6.2:Scala中,子類可以覆蓋父類的val field,而且子類的val field還可以覆蓋父類的val field的getter方法;只要在子類中使用override關鍵字即可;
        class Person {

          val name : String = "張三";
          def age : Int = 0;
        }
        //創建一個Student類來繼承Person類:    
        class Student extends Person{

          override val name : String = "李四";

          override val age : Int = 20;
        }    
        //創建一個Object來調用創建的Student或者Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var student = new Student();
            println("my name is : " + student.name +   ",my age is :" + student.age);
          }
        }
7:isInstanceOf和asInstanceOf,如果我們創建了子類的對象,但是又將其賦予了父類類型的變量。則在后續的程序中,我們又需要將父類類型的變量轉換為子類類型的變量。首先,需要使用isInstanceOf判斷對象是否是指定類的對象,如果是的話,則可以使用asInstanceOf將對象轉換為指定類型。
    注意:如果對象是null,則isInstanceOf一定返回false,asInstanceOf一定返回null;
         如果沒有用isInstanceOf先判斷對象是否為指定類的實例,就直接用asInstanceOf轉換,則可能會拋出異常;
        class Person {

          var name : String = "張三";
          var age : Int = 20;
        }
        //創建一個Student類來繼承Person類:    
        class Student extends Person{

          var sex : String ="男";

        }
        //創建一個Object來調用創建的Student或者Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            //父類的變量引用了子類的對象.
            val p : Person = new Student;
            var s : Student = null;
            //注意是[]不是(),否則報錯。
            if(p.isInstanceOf[Student]){
              s = p.asInstanceOf[Student];
            }
            println(" my name is :"+ s.name + " ,my age is :" + s.age)
          }
        }
8:getClass和classOf,isInstanceOf只能判斷出對象是否是指定類以及其子類的對象,而不能精確判斷出,對象就是指定類的對象。如果要求精確的判斷對象就是指定類的對象,那么只能使用使用getClass和classOf了。
    用法如下所示:
        對象.getClass可以精確獲取對象的類,classOf[類]可以精確獲取類沒然后使用==操作符即可判斷:    
        class Student extends Person{

          var sex : String ="男";
        }
        //創建一個Student類來繼承Person類:    
        class Person {

          var name : String = "張三";
          var age : Int = 20;
        }
        //創建一個Object來調用創建的Student或者Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            //父類的變量引用了子類的對象.
            val p : Person = new Student;
            var s : Student = null;
            //注意是[]不是(),否則報錯。
            if(p.isInstanceOf[Student]){
              s = p.asInstanceOf[Student];
            }
            println("======================================================================")
            //注意,isInstanceOf不可以精確判斷是子類還是父類的。
            if(p.isInstanceOf[Person] && p.isInstanceOf[Student]){
              println("0:p指向了Person,也指向了Student");
            }
            println("======================================================================")
            println(" my name is :"+ s.name + " ,my age is :" + s.age)
            if(p.getClass == classOf[Person]){
              //p指向的是Student;
              println("1:getClass的用法:" + p.getClass + ",classOf的用法:" + classOf[Person]);
            }
            println("======================================================================")
            if(p.getClass == classOf[Student]){
              println("2:getClass的用法:" + p.getClass + ",classOf的用法:" + classOf[Student]);
            }
            println("======================================================================")
            val p2 : Person = new Person;
            if(p2.getClass == classOf[Person]){
              //p2指向的是Person;
              println("3:getClass的用法:" + p2.getClass + ",classOf的用法:" + classOf[Person]);
            }
          }
        }
9:使用模式匹配進行類型判斷:
    9.1:在實際開發中,比如Spark的源碼中,大量的地方都是使用了模式匹配的方式進行類型的判斷,這種方式更加的簡潔明了,而且代碼的維護性和可擴展性也很高。
        使用模式匹配,功能性上來說,與instanceOf一樣,也是判斷主要是該類以及該類的子類的對象即可,不是精確判斷的:
        class Student extends Person{

          var sex : String ="男";
        }
        //創建一個Student類來繼承Person類:    
        class Person {

          var name : String = "張三";
          var age : Int = 20;
        }
        //創建一個Object來調用創建的Student或者Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            //父類的變量引用了子類的對象.
            val p : Person = new Student;

            //模式匹配
            p match {
              case person : Person => println("It is Person class");
              case student : Student => println("It is Student class");
              case _ => println("不知道是什么類型的.");
            }
          }
        }
10:Protected關鍵字,跟java一樣,scala中同樣可以使用protected關鍵字來修飾field和method,這樣在子類中就不需要super關鍵字,直接就可以訪問field和method;
    10.1:注意:還可以使用protected[this],則只能在當前子類對象中訪問父類的field和method,無法通過其他子類對象訪問父類的field和method;
        class Person {

          protected var name : String = "張三";
          protected[this] var age : Int = 20;
        }
        //創建一個Student類來繼承Person類:    
        class Student extends Person{

          var sex : String ="男";
          def showPerson(): Unit ={
            println("my name is : " + name);
          }
          def makeFriends(s : Student): Unit ={
            //還可以使用protected(this)則只能在當前子類對象中訪問父類的field和method,
            //無法通過其他子類對象訪問父類的field和method;
            //下面的s.age會報錯的。protected[this] var age : Int = 20;
            //println("my age is : " + age  + ",you age is : " + s.age);
          }
        }
11:調用父類的constructor,在scala中,每個類可以有一個主constructor和任意多個輔助constructor,而每個輔助constructor的第一行都必須是調用其他輔助constructor或者是主constructor;因此子類的輔助constructor是一定不可能直接調用父類的constructor的;
    注意:只能在子類的主constructor中調用父類的constructor,以下這種語法,就是通過子類的主構造函數來調用父類的構造函數;如果是父類中接受的參數,比如name和age,子類中接受時,就不要用任何val或者var來修飾了,否則會認為是子類要覆蓋父類的field;
        class Person(val name : String,val age : Int) {

        }
        //創建一個Student類來繼承Person類:        
        class Student(name : String,age : Int,var score : Int) extends Person(name,age){

          def this(name : String){
            this(name,0,0);
          }
          def this(age : Int){
            this("張三",age,0);
          }
        }
        //創建一個Student類來繼承Person類:    
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var student = new Student("李思思",22,100);
            println(student.name + " "  + student.age + " " + student.score)
            var student2 = new Student("王五");
            println(student2.name + " "  + student2.age + " " + student2.score)
            var student3 = new Student(20);
            println(student3.name + " "  + student3.age + " " + student3.score)

          }
        }
12:匿名子類,在Scala中,匿名子類是非常常見,而且非常強大的。Spark的源碼中也大量使用了這種匿名子類。
    匿名子類,也就是說,可以定義一個類的沒有名稱的子類,並且直接創建其對象,然后將對象的引用賦予一個變量。之后甚至可以將該匿名子類的對象傳遞給其他函數。
        class Person(protected val name : String) {

          def hello()= "hello, I am :" + name;
        }
        //創建一個Student類來繼承Person類:    
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            //匿名內部類
            var p = new Person("張三"){
              override def hello(): String = "匿名內部類,hello :" + name;
            }
            //調用
            println(p.hello());
          }
        }
13:抽象類,如果在父類中,有某些方法無法立即實現,而需要依賴不同的子類來覆蓋,重寫實現自己不同的方法實現。此時可以將父類中的這些方法不給出具體的實現,只有方法簽名,這種方法就是抽象方法;
    13.1:注意:一個類中如果有一個抽象方法,那么類就必須用abstract來聲明為抽象類,此時抽象類是不可以實例化的。
        在子類中覆蓋抽象類的抽象方法時,不需要使用override關鍵字;
        abstract class Person(val name : String) {

          def hello() : Unit;
        }
        //創建一個Student類來繼承Person類:    
        class Student(name : String) extends  Person(name){

          override def hello(): Unit = println("hello : " + name);
        }
        //創建一個Student類來繼承Person類:
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var student = new Student("張三")
            student.hello()
          }
        }
    13.2:抽象field,如果在父類中,定義了field,但是沒有給出原始值,則此field為抽象field;
         抽象field意味着,scala會根據自己的規則,為var或者val類型的field生成對應的getter和setter方法,但是父類中是沒有該field的。
         子類必須覆蓋field,以定義自己的具體field,並且覆蓋抽象field,不需要使用override關鍵字;
        abstract class Person {

          val name : String;
        }    
        //創建一個Student類來繼承Person類:    
        class Student extends  Person{

          val name : String= "張三";
        }
        //創建一個Student類來繼承Person類:    
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var student = new Student
            println(student.name)
          }
        }

5:Scala之面向對象編程之Trait學習筆記:

1:trait基礎知識:
    1.1:將trait作為接口使用:
        a、Scala中的trait是一種特殊的概念,首先我們可以將trait作為接口來使用,此時的trait就與Java中的接口非常類似;
        b、在trait中可以定義抽象方法,就與抽象類中的抽象方法一樣,只要不給出方法的具體體現即可;
        c、類可以使用extends關鍵字來繼承trait,注意,這里不是implement,而是extends,在scala中沒有implmemts的概念,    無論繼承還是trait,統一都是extends;
        d、類繼承trait后,必須實現其中的抽象方法,實現時候不需要使用override關鍵字;
        e、scala不支持對類進行多繼承,但是支持多重繼承trait,使用with關鍵字即可;
        trait Person {

          def hello(name : String);
        }
        //定義一個MakeFriends的trait
        trait MakeFriends {

          def makeFriends(friend: Friend);
        }
        //定義一個類來繼承上面兩個trait
        class Friend(val name : String) extends Person with MakeFriends with Cloneable with Serializable{

          def hello(name: String) = println("my name is : " +name)

          def makeFriends(friend: Friend)  = println("hello,my name is : " + name + ",your name is :" + friend.name);
        }
        //定義一個object來測試實現的類
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var friend = new Friend("張三");
            var friend2 = new Friend("李四");
            friend.hello("李四");
            friend.makeFriends(friend2);
          }
        }
    1.2:在trait中定義具體方法:
        Scala中的trait可以不是只定義抽象方法,還可以定義具體方法,此時trait更像是包含了通用工具方法的東西,有一個專有的名詞來形容這種情況,就是說trait的功能混入了類。舉例來說,trait中可以包含一些很多類都通用的功能方法,比如打印日志等等,Spark中就使用了trait來定義了通用的日志打印方法:
        trait Logger {

          def log(message : String) = println(message);
        }
        //定義一個類來實現trait接口
        class User(val name : String) extends Logger{

          def makeFriends(user: User): Unit ={
            println("hello, i am " + name + " i am nice to meet you :" + user.name);
            log("makeFriends logger User[name="+user.name+"]");
          }
        }
        //定義一個object來測試實現的類
        object HelloWorld {
        
        def main(args: Array[String]): Unit = {
            val user = new User("張三");
            val user2 = new User("李思思");
            user.makeFriends(user2);
          }
        }

1.3:在trait中定義具體字段:   Scala中的triat可以定義具體field,此時繼承triat的類就自動獲得了triat中定義的field,但是這種獲取field的方式與繼承class不同:如果是繼承class獲取的field,實際是定義在父類中的,而繼承triat獲取的field,就直接被添加到了類中。
        trait Person {

          def hello();
          //定義一個field
          val eyeNum : Int =2;
        }
        //定義一個類來實現trait接口
        class Friend(val name : String) extends Person{

          def hello() = println("my name is : " +name + ",and i have : " +eyeNum + "eyes");

        }
        //定義一個object來測試實現的類
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var friend = new Friend("張三");
            friend.hello();
          }
        }
1.4:在trait中定義抽象字段:   Scala中的Triat可以定義抽象field,而Triat中的具體方法則可以基於抽象field來編寫,但是繼承Triat的類,則必須覆蓋抽象field,提供具體的值:
        trait Person {

          //定義一個抽象的field
          val msg : String;
          //Triat中的具體方法則可以基於抽象field來編寫
          def hello(name : String) = println(msg + " ," + name);
        }
        //定義一個類來實現trait接口
        class Friend(val name : String) extends Person{
          //但是繼承Triat的類,則必須覆蓋抽象field,提供具體的值;
          val msg : String = "hello";
          def makeFriends(p : Person)={
            hello(name)
            println("my name is : " + name,",and i want to make friends with you.")
          }
        }
        //定義一個object來測試實現的類
        object HelloWorld {

          def main(args: Array[String]): Unit = {
            var friend = new Friend("張三");
            var friend2 = new Friend("張三");
            friend.makeFriends(friend2)
          }
        }
2:trait高級知識: 2.1:位實例對象混入trait:   有時候,我們可以在創建類的對象的時候,指定該對象混入某個trait,這樣,就只有這個對象混入該trait的方法,而類的其他對象則沒有:
        import scala.util.logging.Logged

        trait MyLogged extends Logged{

          override def log(msg: String): Unit = {
            println("log : " + msg);
          }
        }
        //定義一個類來實現trait接口
        import scala.util.logging.Logged

        class Person(name : String) extends Logged{
          def hello(): Unit ={
            println("hi , i am is :" + name);
          }
          log("hello is invoked");
        }
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            var person1 = new Person("張三");
            person1.hello();
            var person2 = new Person("李思思") with MyLogged;
            person2.hello()
          }
        }
2.2:trait調用鏈:   a、Scala中支持讓類繼承多個Trait后,依次調用多個trait中的同一個方法,只要讓多個trait的同一個方法中,在最后都執行super方法即可;
        b、類中調用多個trait中都有的這個方法時,首先會從最右邊的trait的方法開始執行,然后依次往左執行,形成一個調用鏈條;
        c、這種特性非常強大,其實就相當於設計模式中的責任鏈模式的一種具體實現依賴;
        trait Handler {

          def handler(data : String){}
        }
        //定義一個trait來實現trait接口
        trait DataValidHandler extends Handler{

          override def handler(data: String): Unit = {
            println("check data : " + data);
            super.handler(data)
          }
        }
        //定義一個trait來實現trait接口
        trait SignatureValidHandler extends Handler{

          override def handler(data: String): Unit = {
            println("signature : " + data)
            super.handler(data)
          }
        }
        //定義一個類來實現trait接口
        class Person(val name : String) extends SignatureValidHandler with DataValidHandler{

          def hello(): Unit ={
            println("hello :" + name);
            handler(name);
          }
        }
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            val p1 = new Person("張三");
            p1.hello();
          }
        }
2.3:在trait中覆蓋抽象方法【注意語法】:   在Trait中,是可以覆蓋父trait的抽象方法的。但是覆蓋時,如果使用了super.方法的代碼,則無法通過編譯。因為super.方法就會去掉用父trait的抽象方法,此時子trait的該方法還是會被認為是抽象的。此時如果要通過編譯,就得給子trait的方法加上abstract override修飾:
        trait MyLogged extends Logged{

          abstract override def log(msg: String): Unit = {
            super.log(msg)
          }
        }
2.4:混合使用trait的具體方法和抽象方法:   trait Valid {

          def getName : String;
          def valid : Boolean = {
            getName == "張三";
          }
        }
        //定義一個類來實現trait接口
        class Person(val name : String) extends Valid{

          println(valid)

          def getName = name;
        }
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            var p1 = new Person("張三");

          }
        }
2.5:trait的構造機制:   在Scala中,trait也是有構造方法的,也就是trait中的,不包含在任何方法中的代碼。而繼承了trait的類的構造機制如下所示:
        a、父類的構造函數執行。
        b、trait的構造代碼執行,多個trait從坐到右依次執行。
        c、構造trait的時候會先構造父類trait,如果多個trait繼承同一個父trait,則父trait只會構造一次。
        d、所有trait構造完畢以后,子類的構造函數執行。
        trait Logger {

          println("logger constructor")
        }
        //定義一個trait來實現trait接口
        trait Mylogger extends Logger{

          println("Mylogger constructor")
        }    
        //定義一個trait來實現trait接口
        trait TimeLogger extends Logger{

          println("TimeLogger constructor");
        }
        //定義一個class
        class Person{
            
          println("person construcotr")
        }
        //定義一個class繼承類和trait
        class Student extends Person with Mylogger with TimeLogger{

          println("Student constructor")
        }
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            val s1 = new Student();
          }
        }
2.6:trait字段的初始化:   在Scala中,trait也是有構造方法的,也就是trait中的,不包含在任何方法中的代碼。而繼承了trait的類的構造機制如下所示:
        a、父類的構造函數執行。
        b、trait的構造代碼執行,多個trait從坐到右依次執行。
        c、構造trait的時候會先構造父類trait,如果多個trait繼承同一個父trait,則父trait只會構造一次。
        d、所有trait構造完畢以后,子類的構造函數執行。
        trait Logger {

          println("logger constructor")
        }
        //定義一個trait來實現trait接口
        trait Mylogger extends Logger{

          println("Mylogger constructor")
        }    
        //定義一個trait來實現trait接口
        trait TimeLogger extends Logger{

          println("TimeLogger constructor");
        }
        //定義一個class
        class Person{
            
          println("person construcotr")
        }
        //定義一個class繼承類和trait
        class Student extends Person with Mylogger with TimeLogger{

          println("Student constructor")
        }
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            val s1 = new Student();

          }
        }
2.7:讓trait繼承類:    在Scala中,trait是沒有接受參數的構造函數的,這是trait與class的唯一區別,但是如果需求就是要trait能夠對field進行初始化,那么只能使用Scala中非常特殊的一種高級特性--提前定義:
        //1:第一種方式實現:
        trait Hello {

          val msg : String;
          println(msg.toString);
        }
        //定義一個class繼承類和trait
        class Person extends {

          val msg : String = "init";
        }with Hello{}
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            var p1 = new Person();
          }
        }
        //2:第二種方式實現:
        trait Hello {

          lazy val msg : String = null;
          println(msg.toString);
        }
        //定義一個class繼承類和trait
        class Person extends Hello{

          override lazy val msg: String = "init"
        }
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            var p1 = new Person();
          }
        }
    2.7:讓trait繼承類:
        在Scala中,trait也可以繼承自class,此時這個class就會成為所有繼承該trait的類的父類;
        class MyUtil {

          def printMessage(msg : String) = println("msg : " + msg);
        }    
        //定義一個trait實現class
        trait Logger extends MyUtil{

          def log(msg : String) = printMessage("log :" + msg);
        }
        //定義一個class實現trait
        class Person(val name : String) extends Logger{

          def hello(): Unit ={
            log("hi , i am : " + name);
            printMessage("hi , i am : " + name);
          }
        }
        //定義一個object來測試實現的類
        object Object {

          def main(args: Array[String]): Unit = {
            val p = new Person("張三");
            p.hello()
          }
        }

 

 

待續......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM