Scala(二)面向对象编程之类、对象(Object)、继承、Trait

  1. 一个简单的类

    1. Scala类的字段声明与getter和setter方法

      package com.lyz
      
      object ClassLearn {
        def main(args: Array[String]): Unit = {
          val helloWord = new HelloWord()
          helloWord.name = "l4" //setter方法 ,如果类中的filed类型是val,则该语句报错
          helloWord.hello()
          println(helloWord.name) //getter方法
        }
      }
      
      class HelloWord {
        /**
          * 1.如果该field是var类型的变量,并且没有被修饰,那么该类就会默认提供public的getter和setter方法,
          * 2.如果该field是var类型的变量,被private修饰,那么该类会自动生成默认的private的getter和setter方法
          * 3.如果该field是val类型的,只会生成默认的getter方法
          * 4.如果不想生成getter和setter方法则将该field声明成private
          * 5.如果想要field只能在本类中访问,不想被其他对象访问到,就用private[this]去修饰
          */
        var name = "z3"
      
        def hello(): Unit = {
          println("hello" + name)
        }
      
        def getName: String = name //自定义的getter方法
      
        def name_=(name: String): Unit = { //自定义setter方法,
          this.name = name
        }
      }
    2. Scala类中利用注解生成Java形式的getter好setter方法

      import scala.beans.BeanProperty
      
      object HelloWordJavaGetSet {
        def main(args: Array[String]): Unit = {
          val helloWordJavaGetSet = new HelloWordJavaGetSet
      
          helloWordJavaGetSet.setName("aaa")
          helloWordJavaGetSet.getName
        }
      }
      
      class HelloWordJavaGetSet {
      
        @BeanProperty //这个注解就能是这个field生成Java风格的getter和setter方法
        var name: String = "z3" //如果field是var的数据类型,那么getter和setter方法都会有,如果为val,那么就只有getter方法
      }
      
    3. Scala类的主构造方法和辅助构造方法

      object HelloWordConstructor {
        def main(args: Array[String]): Unit = {
          val helloWordConstructor = new HelloWordConstructor("l4", 20)
          println(helloWordConstructor.name)
          println(helloWordConstructor.age)
      
      
          val helloWordConstructor1 = new HelloWordConstructor("z4")
          println(helloWordConstructor1.name)
      
          val helloWordConstructor2 = new HelloWordConstructor(10)
          println(helloWordConstructor2.age)
          println(helloWordConstructor2.name)
      
        }
      }
      
      /**
        * 1.Scala里的构造方法分为主构造方法,辅助构造方法。跟类名在一起的就是主构造方法,在代码块里的代码是辅助构造方法。
        * 2.默认的主构造方法为无参的。
        * 3.声明辅助构造方法的时候必须先调用住构造方法。辅助构造方法名为 this 关键字
        * 4.主构造方法可以有默认值,那么我们创建对象的时候就可以不用传递参数,默认使用默认值。如果参数没有指定var或者val,那么默认的是private val
        * 5.主构造方法里的参数如果没有任何修饰符修饰,并且类里边没有任何方法用到该参数,那么类里就不会有该field,如果类中用到了该参数,则会被声明private[this] field
        * 6.如果不带val或var,则下面的name和age,是不可变的字段,而这个两个字段都是对象私有的相当于private[this]val ,那么子类是继承不到这俩个属性的
        */
      
      class HelloWordConstructor(var name: String = "z3", var age: Int = 20) {
      
        //辅助构造函数
        def this(name: String) {
          this(name, 0)
        }
      
        //辅助构造函数
        def this(age: Int) {
          this(null, age)
        }
      }
  2. 内部类:Scala内部类跟Java内部类有这本质的区别,因为Java内部类从属于外部类,而Scala内部类从属于外部类的具体实例

    1. Java内部类

      package com.lyz.java;
      
      import com.sun.org.apache.bcel.internal.classfile.InnerClass;
      import java.util.ArrayList;
      import java.util.List;
      /**
       * Java内部类,内部类从属于外部类,不属于每个外部类的实例
       */
      public class OutClass {
          List<InnerClass> list = new ArrayList<>();
          //内部类,从属于外部类,不去分外部的具体示例
          class InnerClass {
          }
          public InnerClass getInnerClass() {
              return new InnerClass();
          }
          public static void main(String[] args) {
      
              //创建外部类具体实例 outClass
              OutClass outClass = new OutClass();
              //根据外部类创建具体的内部类实例innerClass
              InnerClass innerClass = outClass.getInnerClass();
      
              //创建外部类具体实例 outClass1
              OutClass outClass1 = new OutClass();
              //根据外部类创建具体的内部类实例innerClass1
              InnerClass innerClass1 = outClass.getInnerClass();
      
              /**
               * outClass将innerClass1添加到outClass里的集合里不会报错,因为java内部类不属于外部类具体的实例
               */
              outClass.list.add(innerClass1);//
              /**
               * outClass1将innerClass添加到outClass1里的集合里不会报错,因为java内部类不属于外部类具体的实例
               */
              outClass1.list.add(innerClass);
          }
      }
      
    2. Scala内部类

      package com.lyz.scala
      import scala.collection.mutable.ArrayBuffer
      
      object OutClass {
        def main(args: Array[String]): Unit = {
          //创建外部类具体实例 outClass
          val outClass = new OutClass
          //根据外部类创建具体的内部类实例innerClass
          val innerClass = outClass.getInner
      
          //创建外部类具体实例 outClass1
          val outClass1 = new OutClass
          //根据外部类创建具体的内部类实例innerClass1
          val innerClass1 = outClass1.getInner
      
          /**
            * outClass将innerClass添加到outClass里的集合里不会报错,因为Scala内部类属于外部类具体的实例
            */
          outClass.array += innerClass
      
          /**
            * outClass1将innerClass1添加到outClass1里的集合里不会报错,因为Scala内部类属于外部类具体的实例
            */
          outClass1.array += innerClass1
      
          /**
            * outClass将innerClass1添加到outClass里的集合里会报错,因为Scala内部类属于外部类具体的实例
            */
          outClass.array += innerClass1
      
          /**
            * outClass1将innerClass添加到outClass1里的集合里会报错,因为Scala内部类属于外部类具体的实例
            */
          outClass1.array += innerClass
        }
      }
      
      
      //外部类
      class OutClass {
      
        val array = new ArrayBuffer[Inner]()
      
        //内部类,它从属于外部类的具体事例,跟java有本质上的区别
        class Inner {}
      
        def getInner: Inner = {
          new Inner
        }
      
      }
      
    3. Scala扩大内部类的作用域:利用外部类的伴生对象就可以扩大内部类的作用域,而使内部类从属于外部类,而不是丛属于外部类的具体实例

      //外部类
      class OutClass {
        val array = new ArrayBuffer[OutClass.Inner]()
        def getInner: OutClass.Inner = {
          new OutClass.Inner
        }
      }
      object OutClass{
        //内部类,定义在外部类的伴生对象中,那么该内部类就从属于外部类,而不是从属于外部类的具体实例
        class Inner {}
      }
    4. Scala内部类使用外部类的引用

      //外部类
      class OutClass {
        outer => //声明外部类的引用提供给内部类使用
      
        val name: String = "l4"
        val array = new ArrayBuffer[Inner]()
        //内部类,它从属于外部类的具体事例,跟java有本质上的区别
        class Inner {
          def hello(): Unit = {
            outer.name //内部类使用外部类的引用
          }
        }
        def getInner: Inner = {
          new Inner
        }
      }
      
  3. Object:在Scala中没有像Java一样的static语法,但是Scala中的Object类似于Javastatic,它是一个单利,里边放静态的fieldmethod。第一次调用object的时候就会执行objectconstructor方法,也就是不在method代码中的代码,注意,只有第一次调用object的时候,才会执行constructor方法。并且object的constructor没有参数构造方法,只有无参构造方法。

    1. 简单的object对象

      package com.lyz.scala
      
      /**
        * 打印结果为:调用object的时候时候知否执行了除了method代码块意外的代码???????
        *           l4
        *           l4
        * 总结:只有第一次调用object对象的时候才会调用方法除外的代码,来初始化object的constructor
        */
      object ObjectLearn {
        def main(args: Array[String]): Unit = {
          println(ObjectTest.getName)
          println(ObjectTest.getName)
        }
      }
      object ObjectTest{
        private var name:String="l4"
        println("我是方法以为的代码。。。。。。。。。。。。。。。。。")
        def objectMethod(): Unit ={
          println("是否执行method方法???????????????")
        }
        def getName:String=name
      }
      
    2. 伴生对象与伴生类:如果有一个class和一个跟class同名的object,那么我们就可以管class叫做object的伴生类,管objectclass的伴生对象。伴生类与伴生对象一定要在同一个.scala文件中。伴生类和伴生对象最大的特点就是可以互相访问彼此的私有属性。这种功能是满足类似于Java里的类既有静态变量又有普通变量

      package com.lyz.scala
      
      object ObjectLearn {
        def main(args: Array[String]): Unit = {
          val objectTest = new ObjectTest
          objectTest.helloPrivate
      
          println(ObjectTest.helloPrivate)
        }
      }
      
      object ObjectTest {
        private var name: String = "l4"
        private var name1: String = "z4"
        def helloPrivate: Unit = {
          //调用伴生类的私有属性,,如果不是伴生类,那么是访问不到私有属性的,打印结果为 10
          val objectTest = new ObjectTest
          println(objectTest.age)
        }
      }
      
      class ObjectTest {
        private var age: Int = 10
        def helloPrivate: Unit = {
          //调用伴生对象的私有属性,如果不是伴生对象,那么是访问不到私有属性的,打印结果z4
      
          println(ObjectTest.name1) 
        }
      }
    3. apply方法:他是object里的一个重要方法,通常在伴生对象中实现这个方法,来实现创建伴生类的对象的功能,而创建伴生类的对象时我们通常不会使用new Class来创建,而是使用Class()这种方式来隐式的调用object里的apply方法来创建伴生类的对象。,使我们创建伴生类的对象更加简洁。特别注意的是。spark里用到了很多这样的语法

      ```
      package com.lyz.scala
      
      object ApplyLearn {
        def main(args: Array[String]): Unit = {
          val applyTest = ApplyTest("wxm", 25) //隐式调用伴生对象的apply方法
          println(applyTest.age, applyTest.name)
        }
      }
      
      class ApplyTest(var name: String = "lyz", var age: Int = 10)
      
      object ApplyTest {
        def apply(name: String = "lyz", age: Int = 10): ApplyTest = new ApplyTest(name, age) //apply方法
      }
      ```
      
  4. 枚举

    package com.lyz.scala
    
    //Scala里的枚举定义
    object ObjectEnum extends Enumeration {
      val APRING = Value(1,"夏天") //利用Value初始化枚举值
    }
    
    
    object EnumTest{
      def main(args: Array[String]): Unit = {
        println(ObjectEnum.APRING.id) //获取枚举code
        println(ObjectEnum.APRING.toString)//获取枚举名
      }
    }
  5. 继承Scala里的继承跟Java里的继承类似,都是用extends关键字,子类继承父类的属性或者方法,如果父类的filed或者method是私有的、final的,那么子类是继承不了这些属性或者方法的。如果父类是用final修饰的,则子类是不能继承的。如果子类继承了父类的,可以覆盖父类的方法,必须用override的关键字,如果想要调用父类的方法可以用super关键字

    1. override field:子类可以覆盖父类的val属性,也可以覆盖父类的getter方法

      package com.lyz.scala
      
      object ExtendsLearn {
        def main(args: Array[String]): Unit = {
          val extendsTestSub = new ExtendsTestSub
          println(extendsTestSub.name) //打印结果是 z3
          println(extendsTestSub.age) // 打印结果为 30
        }
      }
      
      class ExtendsTest{
        val name:String = "l4"
        def age:Int=20
      }
      
      class ExtendsTestSub extends ExtendsTest {
        override val name:String="z3" //覆盖父类的name属性
        override def age: Int = 30 //覆盖父类的age的getter方法
      }
      
    2. isInstanceOfasInstanceOf:判断类的实际类型,如果对象为null那么isInstanceOf返回的一定是falseasInstanceOf返回的也一定是false

      package com.lyz.scala
      
      object InstanceOfLearn {
        def main(args: Array[String]): Unit = {
          val instanceOfTest:InstanceOfTest = new InstanceOfTestSub //子类对象执行父类引用
      
          if(instanceOfTest.isInstanceOf[InstanceOfTestSub]){ //判断父类的实际类型
            var s  = instanceOfTest.asInstanceOf[InstanceOfTestSub]//转换成实际类型
            println(s)
          }   
        }
      }
      
      class InstanceOfTest{}  
      class InstanceOfTestSub extends InstanceOfTest {}
      
    3. getClassclassOfisInstanceOf是有局限性的,只能判断出对象是否是指定类以及其子类的对象,而不能精确的判断出指定类的对象

      package com.lyz.scala
      
      object InstanceOfLearn {
        def main(args: Array[String]): Unit = {
      
          val instanceOfTest: InstanceOfTest = new InstanceOfTestSub //子类对象执行父类引用
      
          /**
            * 利用isInstanceOf判断具体的类型
            */
          println(instanceOfTest.isInstanceOf[InstanceOfTestSub]) //返回为true
          println(instanceOfTest.isInstanceOf[InstanceOfTest]) //返回为true
      
          /**
            * 利用getClass和classOf判断具体的类型
            */
          println(instanceOfTest.getClass == classOf[InstanceOfTestSub]) //返回为true
          println(instanceOfTest.getClass == classOf[InstanceOfTest]) //返回为false
        }
      }
      
      class InstanceOfTest {}
      
      class InstanceOfTestSub extends InstanceOfTest {}
      
    4. match:模式匹配判断类的类型,他跟isInstanceOf的功能是一样的,不是精确判断类的类型,spark里用到了很多这样的语法

      package com.lyz.scala
      
      object InstanceOfLearn {
        def main(args: Array[String]): Unit = {
      
          val instanceOfTest: InstanceOfTest = new InstanceOfTestSub //子类对象执行父类引用 
          instanceOfTest match {
            case sub: InstanceOfTestSub => println(sub) //判断是否是InstanceOfTestSub类型
            case _ =>println("unknown type") //其他类型 利用 _ 来代替其他类型
      
          }
        }
      }
      
      class InstanceOfTest {}
      class InstanceOfTestSub extends InstanceOfTest {}
      
    5. 子类调用父类的构造函数:子类调用父类的构造函数一定是在子类的主构造函数里调用父类的构造函数,因为子类的辅助构造函数的第一行一定是调用他自己的主构造函数。特别注意的是如果父类中接受的参数,比如下边的nameage,在子类接收时就不要指定val或者var来修饰了,如果指定的话,会认为子类的会覆盖父类的field,他会强制要求你加上override关键字来覆盖父类的属性

      //父类
      class InstanceOfTest(val name:String,val age:Int) {}
      
      //子类,接受父类的参数是一定不要修饰成val或者var,否则就会认为父该父类的field,他会强制要求你加上override关键字来覆盖父类的属性
      class InstanceOfTestSub(name:String,age:Int,var score:Double) extends InstanceOfTest(name,age) {
      
        //子类的辅助构造函数,一定不能调用父类的构造函数,因为子类的辅助构造函数第一行一定是调用他自己的主构造函数
        def this(name:String){
          this(name,0,0.0) //调用主构造函数
        }
      }
      
    6. 匿名内部类:定义一个没有名字的子类,并直接创建对象,将其对象赋予一个变量。之后也可以将匿名子类的对象传递给其他函数。

      package com.lyz.scala
      
      /**
        * 类的注释
        *
        * @Package com.lyz.scala
        * @ClassName InstanceOfLearn
        * @Description ${TODO}
        * @Author liyuzhi
        * @Date 2018-06-11 22:13
        */
      
      
      object InstanceOfLearn {
        def main(args: Array[String]): Unit = {
      
          //匿名内部类
          val p = new InstanceOfTest("aaa", 10) {
            override def hell: Unit = {
              println("bbbbbb")
            }
          }
          println(p.hell) //打印结果为 bbbbb
        }
      }
      
      //父类
      class InstanceOfTest(val name: String, val age: Int) {
        def hell: Unit = {
          println("aaaaaaaaaaaa")
        }
      }
      
    7. 抽象类:利用abstract修饰的类就是抽象类,抽象类里的方法可以没有实现也可以有实现,如果方法有abstract修饰,那么这个类一定是抽象类,抽象类里包含抽象方法和抽象属性,抽象方法没有实现,抽象属性没有默认值,子类集成抽象类必须实现抽象类的方法以及给属性的复制,在scala中继承抽象类覆盖方法或者覆盖属性不需要override关键字

  6. Trait:是一个关键字,用于修饰类,他是一个特殊的概念

    1. Trait作为接口来使用:我们可以将Trait作为接口来使用,它和Java中的接口非常相似。在Trait中可以定义抽象方法,他与抽象类中的方法一样,没有具体的实现,继承该Trait的类必须实现抽象方法否则该类也要变成抽象类。在Trait中也可以定义具体的方法,继承自该Trait的类就会直接添加了该方法。在Trait中也可以定义抽象属性,继承该Trait的类必须初始化抽象属性。在Trait中也可以定义具体的属性,继承自该Trait的类就直接添加了该属性。。继承Trait使用关键字extendsscala中跟Java一样不支持多继承,但是可以多重继承,使用with关键字

      package com.lyz.scala
      
      object TraitLearn {
        def main(args: Array[String]): Unit = {
          val traitTest = new TraitTestClass
          println(traitTest.helloName())
          println(traitTest.helloAge())
          println(traitTest.log())
        }
      }
      
      trait TraitTest {
        //抽象属性
        var name: String
      
        //抽象方法
        def helloName(): Unit
      }
      
      trait TraitTest1 {
        //抽象属性,scala中未被初始化的属性就是抽象属性
        val age: Int
      
        //具体的属性
        val score: Double = 10.0
      
        //抽象类,scala中未被实现的方法就是抽象方法
        def helloAge(): Unit
      }
      
      trait TraitTest2 {
        //带有实现的方法
        def helloInit(msg: String): Unit = {
          println("I have method implements :" + msg)
        }
      }
      
      class TraitTestClass extends TraitTest with TraitTest1 with TraitTest2 {
        //继承自TraitTest的属性,必须赋值
        var name: String = "l4"
      
        //继承自 TraitTest 的方法,必须实现
        def helloName(): Unit = println("name is :" + name)
      
        //继承自 TraitTest1 的属性,必须赋值
        val age: Int = 10
      
        //继承自 TraitTest1 的方法,必须实现
        def helloAge(): Unit = println("age is :" + age)
      
        def helloScore(): Unit = {
          //1.继承 TraitTest1 的Score属性,继承Trait的方式跟继承Class方式不同,
          //2.Trait继承方式直接就将该属性直接添加到了这个类中了
          println("score is :" + score)
        }
      
        def log(msg: String = "I am is log"): Unit = {
          //调用 TraitTest2 的方法
          helloInit(msg)
        }
      }
      
    2. 为实例混入Trait:当我们创建对象的时候,根据业务需求需要添加为对象添加一个额外的功能,那么我们就可以混入一个Trait来拓展这对象的功能。注意只有混入Trait的对象才会有Trait里的功能,其他对象没有。它是作用在具体的对象上边的

      package com.lyz.scala
      
      object TraitLearn3 {
        def main(args: Array[String]): Unit = {
          //第一种情况:他会默认调用TraitTest3的helloName方法,他是个空实现
          val traitTestClass1 = new TraitTestClass1
          traitTestClass1.helloClass()
      
          //第二种情况:混入一个Trait,他就调用TraitTest4覆盖以后的helloName方法
          val traitTestClass2 = new TraitTestClass1 with TraitTest4
          traitTestClass2.helloClass()
        }
      }
      
      trait TraitTest3 {
        def helloName(): Unit = {}
      
      }
      
      trait TraitTest4 extends TraitTest3 {
        override def helloName(): Unit = {
          println("helloname is bean invoked !")
        }
      }
      
      class TraitTestClass1 extends TraitTest3 {
        def helloClass(): Unit = {
          helloName()
        }
      }
      
    3. Trait的调用链:Scala中支持在类继承多个Trait以后,一次调用多个Trait的同一个方法,前提是多个Trait的同一个方法中的最后一行要调用父类的同一个方法。调用顺序是从最左边的Trait开始执行,一次一个调用链。

      package com.lyz.scala
      
      object TraitLearn4 {
        def main(args: Array[String]): Unit = {
          /**
            * 打印结果为:
            *           i am is TraitTest7.......l4
            *           i am is TraitTest6.......l4
            */
          val traitTest8 = new TraitTest8
          traitTest8.helloName("l4")
        }
      }
      
      trait TraitTest5 {
        def helloName(name:String): Unit = {
        }
      }
      
      trait TraitTest6 extends TraitTest5 {
      
        //覆盖父类的相同方法
        override def helloName(name:String): Unit = {
          println("i am is TraitTest6......."+name)
          //调用父类的相同方法
          super.helloName(name)
        }
      }
      
      trait TraitTest7 extends TraitTest5 {
        //覆盖父类的相同方法
        override def helloName(name:String): Unit = {
          println("i am is TraitTest7......."+name)
          //调用父类的相同方法
          super.helloName(name)
        }
      }
      
      class TraitTest8 extends TraitTest6 with TraitTest7 {
        def hello(name:String): Unit = {
          println("helloname is bean invoked !")
          //调用多个Trait的相同方法,首先执行的是 TraitTest7里的 helloName 方法,然后执行的是 TraitTest6里的 helloName 方法
          helloName(name)
        }
      }
    4. Trait里的抽象方法和具体的方法混合使用,这是一种模板模式

      package com.lyz.scala
      
      object TraitLearn9 {
        def main(args: Array[String]): Unit = {
          /**
            * 打印结果为:
            * l4
            * true
            */
          val traitTest11: TraitTest10 = new TraitTest11
          println(traitTest11.helloName("l4"))
          println(traitTest11.conversion("l4"))
      
        }
      }
      
      trait TraitTest10 {
        def helloName(name: String): String //抽象类
        def conversion(name: String): Boolean = { //依赖于抽象类的具体类
          helloName(name) == "l4"
        }
      }
      
      //集成Trait,覆盖helloName方法,继承conversion方法
      class TraitTest11 extends TraitTest10 {
        override def helloName(name: String): String = "l4"
      }
      
    5. Trait的构造机制:Trait也是有构造代码的,他的构造代码就是除方法以外的代码都是Trait的构造代码

    6. 继承Trait的构造机制:

      1. 父类的构造代码先执行
      2. Trait的构造代码执行,如果多个Trait,那么依次从左到右依次执行Trait的构造代码
      3. 构造Trait的时候首先构造Trait的父类,如果多个Trait继承同一个Trait,那么父Trait只会构造依一次
      4. 所有Trait构造完毕以后,那么才会构造子类,执行子类的构造代码
    7. Trait的字段初始化:在Trait中是没有参数构造方法的,如果想初始化字段值那么就需要用到Scala高级语法

      package com.lyz.scala
      
      object TraitLearn001 {
        def main(args: Array[String]): Unit = {
      
          //第一种方法
          class TraitTest0001
          val traitTest001 = new {
            val name: String = "l4"
          } with TraitTest0001 with TraitTest001
          println(traitTest001.name)
      
          val traitTest00001 = new TraitTest00001()
          traitTest00001.name
      
          val traitTest000011111 = new TraitTest000011111
          traitTest000011111.name
      
        }
      }
      
      trait TraitTest001 {
        //抽象属性
        val name: String
      
        println(name.toString)
      }
      
      //第二种方法
      class TraitTest00001 extends {
        val name: String = "l4"
      } with TraitTest001
      
      //第三种方法,利用lazy关键字,lazy只能修饰val变量
      class TraitTest000011111 extends TraitTest001{
        override lazy val name: String = "l4"
      }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值