美文网首页
scala(九) 封装、继承与多态

scala(九) 封装、继承与多态

作者: 万事万物 | 来源:发表于2021-06-24 06:48 被阅读0次

    封装

    封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
    java封装操作如下:

    1. 将属性进行私有化
    2. 提供一个公共的set方法,用于对属性赋值
    3. 提供一个公共的get方法,用于获取属性的值

    定义一个对象

    class Person{
      /**
       * id
       */
      private var id:Int=_
      /**
       * 姓名
       */
      private var name:String=_
      /**
       * 年龄
       */
      private var age:Int=_
      /**
       * 性别
       */
      private var sex:Char=_
    
    }
    

    在 java 中可以通过第三方工具快速生成 get/set方法;在scalal中(idea)不行。若需要添加get/set方法需要自己写。
    如:

      def getId():Int=this.id 
      
      def setId(id:Int)=this.id=id
    

    在scala 为了符合JavaBean规范,专门提供了一个 注解 @BeanProperty 用于对属性生成 getter/setter 方法。
    使用 @BeanProperty 是有限制的。

    1. 属性不能被 private 修饰
    2. 属性也不能声明成 val
    class Person{
      /**
       * id
       */
      @BeanProperty var id:Int=_
      /**
       * 姓名
       */
      @BeanProperty var name:String=_
      /**
       * 年龄
       */
      @BeanProperty var age:Int=_
      /**
       * 性别
       */
      @BeanProperty  var sex:Char=_
      
    }
    

    使用

      def main(args: Array[String]): Unit = {
        val p=new Person()
        p.setId(1001)
        p.setName("张三")
        p.setAge(19)
        p.setSex('男')
    
        val info=s"id=${p.getId},姓名=${p.getName},年纪=${p.getAge},性别=${p.getSex}"
    
        println(info) // id=1001,姓名=张三,年纪=19,性别=男
    
      }
    

    疑问?使用get/set 不就是用来访问和操作私有属性的吗? 使用 @BeanProperty 居然还必须时 public 那么定义该注解的有何用?
    如下:不使用 get/set 可以进行操作。

      def main(args: Array[String]): Unit = {
        val p=new Person()
        p.id=1001
        p.name="张三"
        p.age=19
        p.sex='男'
    
        val info=s"id=${p.id},姓名=${p.name},年纪=${p.age},性别=${p.sex}"
    
        println(info) // id=1001,姓名=张三,年纪=19,性别=男
      }
    

    有没有小伙伴和我有一样的疑问?
    @BeanProperty 只是用于符合JavaBean规范,java很多api都遵循这个规范,scala若要去调用,也不得不去准寻这规范。严格意义上来说,scala的封装并不是封装。

    还是用个案例来说明吧。json 可以用于将 JavaBean转为JSON或者将 JSON格式数据转为JavaBean。

    导入 fastjson

    <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.72</version>
    </dependency>
    

    对象 转 json

      def main(args: Array[String]): Unit = {
        val p=new Person()
        p.id=1001
        p.name="张三"
        p.age=19
        p.sex='男'
        
        // 对象 转 json 
        val jsonStr=JSON.toJSON(p).toString 
    
        println(jsonStr) // {"sex":"男","name":"张三","id":1001,"age":19}
      }
    

    若没有 get/set 方法,将无法解析
    json 转对象

      class Person{
        /**
         * id
         */
         var id:Int=_
        /**
         * 姓名
         */
         var name:String=_
        /**
         * 年龄
         */
         var age:Int=_
        /**
         * 性别
         */
          var sex:Char=_
      }
    
      def main(args: Array[String]): Unit = {
        // 对象 转 json  
        //  classOf[Person] 用于获取  Person 的 class
        val p:Person=JSON.parseObject("""{"sex":"男","name":"张三","id":1001,"age":19}""",classOf[Person])
    
        val info=s"id=${p.id},姓名=${p.name},年纪=${p.age},性别=${p.sex}"
    
        println(info) // id=0,姓名=null,年纪=0,性别= 
    
      }
    

    重新指定 @BeanProperty

    class Person{
        /**
         * id
         */
        @BeanProperty var id:Int=_
        /**
         * 姓名
         */
        @BeanProperty var name:String=_
        /**
         * 年龄
         */
        @BeanProperty var age:Int=_
        /**
         * 性别
         */
        @BeanProperty var sex:Char=_
    
      }
    
      def main(args: Array[String]): Unit = {
        // 对象 转 json  
        //  classOf[Person] 用于获取  Person 的 class
        val p:Person=JSON.parseObject("""{"sex":"男","name":"张三","id":1001,"age":19}""",classOf[Person])
    
        val info=s"id=${p.id},姓名=${p.name},年纪=${p.age},性别=${p.sex}"
    
        println(info) // id=1001,姓名=张三,年纪=19,性别=男
    
      }
    

    关于 @BeanProperty 注解,我也不太明白为什么开发者会这么设计,对于我来说就是不合理。但他的作用就是用于兼容JavaApi。


    继承

    java中的继承
    语法:

    [修饰符] class 类名 extends 父类名{类体}

    特性:

    1. 使用 extends 关键字用于继承
    2. 被标识为 final的类不能被继承,
    3. 只能单继承
    4. 被继承的类可以获取它所有非 private 修饰的属性和方法。
    5. 子类可以重写父类的方法

    scala中的继承
    语法:

    class 类名[(参数列表)] extends 父类名[参数列表]

    特性:

    1. 使用 extends 关键字用于继承
    2. 同java一致,scala也只能用于单继承。
    3. extends 并不是继承一个类的标志,也可用于 特质类上。
    4. private 修饰的不能被继承。

    定义

      class Person{
        
        var name="your name?"
        
        def add(x:Int,y:Int)=x+y
        
      }
    
      class Student extends Person 
    
    

    调用

      def main(args: Array[String]): Unit = {
        val stu=new Student
        println(stu.name) //  your name?
    
        println(stu.add(1,2)) // 3
      }
    

    方法重写:
    Person 中有个add 方法,可能该方法并不是我们想要,我需要的功能是 x-y 此时就需要对该方法进行重写。
    在scala 中重写父类中的方法,需要使用 override 关键字修饰。

      class Student extends Person {
        // 重写 父类的 add 方法
        override def add(x: Int, y: Int): Int = x-y
      }
    

    调用:

      def main(args: Array[String]): Unit = {
        val stu=new Student
        println(stu.name) // your name?
    
        println(stu.add(1,2)) // -1
      }
    

    属性重写
    在scala中除了可以对方法进行重写外,还可以对属性进行重写

      class Student extends Person {
        
        name="王富贵"
    
        override def add(x: Int, y: Int): Int = x-y
    
      }
      def main(args: Array[String]): Unit = {
        val stu=new Student
        println(stu.name) // 王富贵
    
        println(stu.add(1,2)) // -1
      }
    

    注意:以上这种不能称为重写,而只是在子类定义了一个与父类相同的属性,覆盖了父类中的属性,可以称为改了父类中该属性的值。
    那么什么才叫属性重写呢?

    1. 父类中的属性是必须定义成 val ,不能使用private 修饰。
    2. 重写属性和重写方法一样,都会用到 override 关键字。

    重新在Person中定义一个属性hobby

      class Person{
    
        var name="your name?"
        val hobby="唱跳,rap"    
        
    
        def add(x:Int,y:Int)=x+y
    
      }
    

    Student中重新父类的 hobby 属性,比如打篮球

      class Student extends Person {
    
        name="坤坤"
        override val hobby: String = "打篮球"
    
        override def add(x: Int, y: Int): Int = x-y
    
      }
    

    运行

      def main(args: Array[String]): Unit = {
        val stu=new Student
        println(stu.name) // 坤坤
        println(stu.hobby) // 打篮球
    
        println(stu.add(1,2)) // -1
      }
    

    温馨提示:没有冒犯任何姓名中带的人,不过我觉得,能看到此文章的人应该都是程序员吧?没有哪个程序员会是某垃圾的粉丝吧。


    调用父类中的方法
    在java中若要调用父类中的 方法,会使用supper 关键字,在scala中也是一样。

      class Student extends Person {
    
        name="坤坤"
        override val hobby: String = "打篮球"
    
        override def add(x: Int, y: Int): Int = {
          // 调用父类中的 add 方法
          println(s"supper add=:${super.add(x,y)}")
          x-y
        }
    
      }
    

    构造器
    继承中父子类的构造器知识点也是很重要的。

    1. 若父类中声明了主构造器,子类必须向父类的主构造器中传参
      // 父类
      class Person(val id:Int,val name:String){
      }
      // 子类
      class Student(override val id:Int, override val name:String, age:Int) extends Person(id,name){
    
      }
    
    1. 若父类中主构造器无参,副构造器有参数,继承父类时,可以不用对父类构造器赋值;表示默认使用父类的无参构造器。
      class Person{
    
        def this(id:Int,name:String){
          this()
        }
    
      }
    
      class Student(val id:Int, val name:String, age:Int) extends Person{
    
      }
    

    也可以指定父类的副构造器;也是可以的。

      class Person{
    
        def this(id:Int,name:String){
          this()
        }
    
      }
    
      class Student(val id:Int, val name:String, age:Int) extends Person(id,name){
    
      }
    
    1. 调用父类构造器
      在java中,使用 supper 关键字,并且必须指定在子类的第一行。
      如:
    class A{}
    
    class B extends A{
        // 调用父类构造器
        public B(){
            super();
        }    
    }
    

    在scala中也是如此。此时有个,有个问题,在scala中定义副构造器必须调用主构造器或其他副构造器。正常的情况下,this()会放在构造器的第一行,此时需要调用父类的构造器,是super()放在第一行还是this()放在第一行呢?

    答案:都不行,scala不支持这么做。

    多态

    多态:父类的引用指向子类的实例。

      def main(args: Array[String]): Unit = {
        
        val person:Person=new Student()
      }
    

    思考一:person.add(1,2) 调用是父类的还是子类的?

      def main(args: Array[String]): Unit = {
        val person:Person=new Student()
       println(person.add(1,2)) 
      }
    

    不用想,肯定是子类的,上面的案例中,Student 重写了 Personadd方法。所以答案是 -1;这个应该知道。

    思考二:person.name 是 "坤坤" 还是 "your name?"?

      def main(args: Array[String]): Unit = {
    
        val person:Person=new Student()
        println(person.name) 
    
      }
    

    答案是 "坤坤" 在scala 中 属性也具有多态性,这点和java一定要区分开。

    回想java 中,对于方法,运行看右(new 的部分),对于属性,运行看左(引用部分)。

    public class Demo101 {
    
        public static void main(String[] args) {
            A a=new B();
            System.out.println(a.name);
            a.say();
    
        }
    }
    
    class A{
    
        String name="aaaa";
    
        public void say(){
            System.out.println("a...");
        }
    }
    
    class B extends A{
    
        String name="bbbb";
        public void say(){
            System.out.println("b...");
        }
    }
    

    虽然A和B都有 name属性,但是执行中结果仍然是父类A的name,而方法不一样,需要看new出来最终的对象是什么。
    结果:

    aaaa
    b...
    

    这里涉及到了java 中静态分派与动态分派相关的概念

    思考三: person.hobby 又是什么?

    答案:肯定是 Student 中的 "打篮球";这个我就写,可以下去动手试试。

    总结:

    这就是 scala 中的 三大特性继承封装多态
    基本上和java类似,为了区分开的是 java中 属性不具备多态性,scala中属性具备多态性

    相关文章

      网友评论

          本文标题:scala(九) 封装、继承与多态

          本文链接:https://www.haomeiwen.com/subject/dpjpyltx.html