• Scala 基础 (六):面向对象(下篇)


    大家好,我是百思不得小赵。

    创作时间:2022 年 7 月 2 日
    博客主页: 🔍点此进入博客主页
    —— 新时代的农民工 🙊
    —— 换一种思维逻辑去看待这个世界 👀
    今天是加入CSDN的第1217天。觉得有帮助麻烦👏点赞、🍀评论、❤️收藏



    在之前的文章中总结了Scala面向对象模块基础的内容,接下来学习面向对象的高阶内容。

    一、抽象类

    如何定义?

    // 定义抽象类
    abstract class 类名{
    	// 定义抽象属性 不赋初始值
    	val|var 变量名: 类型
    	// 定义抽象方法  只声明,不实现
    	def 方法名(): 返回值类型
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
    • 重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override
    • 子类中调用父类的方法使用 super 关键字
    • 子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;
    • 子类对非抽象属性重写,父类非抽象属性只支持 val 类型,而不支持 var。因为var修饰为可变量,子类继承后可以直接使用修改,没有必要重写。

    举个栗子:

    // 定义抽象类
    abstract class Person{
      // 定义非抽象属性
      val name: String = "person"
      // 抽象属性
      var age: Int
    
      // 非抽象方法
      def eat(): Unit ={
        println("Person eat")
      }
    
      // 抽象方法
      def sleep(): Unit
    }
    
    class Student extends Person{
      // 实现抽象属性和方法
       var age: Int = 18
    
       def sleep(): Unit ={
        println("student sleep")
      }
    
      // 重写非抽象属性和方法
      override val name: String = "Student"
    
      override def eat(): Unit = {
        super.eat()
        println("Student eat")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    匿名子类

    和 Java 一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。匿名子类只针对抽象类和接口。

    举个栗子:

    object Test10_AnnoymousClass {
      def main(args: Array[String]): Unit = {
        // 匿名子类实现
        val person = new Person {
          override var name: String = "alice"
    
          override def eat(): Unit = println("person eat")
        }
    
        println(person.name)
        person10.eat()
      }
    }
    
    abstract class Person {
      var name: String
      def eat():Unit
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    二、单例对象(伴生对象)

    Scala是一门完全面向对象的语言,没有静态操作。为了能够和Java语言进行交互,使用单例对象来取代static关键字的语义,伴生类的静态声明都可放在伴生对象中。

    基本语法:

    object 伴生对象名{
    	val country: String="China"
    }
    
    • 1
    • 2
    • 3
    • 编译后会生成两个类,伴生对象是伴生类(类名为对应类后加$符号)的单例对象。
    • 单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致,必须在同一个文件中。
    • 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
    • Scala 中 obj(arg)的语句实际是在调用该对象的 apply 方法,即 obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
    • Scala底层对于伴生对象的apply方法进行了优化,调用时可以省略方法名,直接使用单例对象的名称+调用apply方法的参数
    • 当使用 new 关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的 apply 方法

    举个栗子:

    // 伴生类
    // 构造器私有化
    class Student private(val name: String, val age: Int) {
      def printInfo(): Unit = {
        println(s"Student11: $name $age ${Student11.school}")
      }
    }
    
    // 伴生对象
    object Student {
      // 看作静态的属性
      val school: String = "清华"
      // 定义类对象实例创建的方法
      def newStudent(name: String,age: Int): Student11 = new Student11(name,age)
    
      def apply(name: String, age: Int): Student11 = new Student11(name, age)
    }
    
    
    object Test11_Object {
      def main(args: Array[String]): Unit = {
        val student = Student.newStudent("alice", 12)
        student.printInfo()
    
        val student1 = Student.apply("haha", 12)
    
        val student2 = Student("lolo", 34)
        student2.printInfo()
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    三、特质

    如何定义特质?

    trait 特质名 {
    	// 代码块
    }
    
    • 1
    • 2
    • 3
    • Scala 语言中,采用特质 trait(特征)来代替接口的概念
    • 多个类具有相同的特征时,就可以将这个特征提取出来,用继承的方式来复用
    • Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类。

    基本语法:

    有父类 class extends baseClass with trait1 with trait2 ... {}
    没有父类 class extends trait1 with trait2 ... {}
    
    • 1
    • 2
    • 如果一个类在同时继承特质和父类时,应当把父类写在 extends 后。
    • 特质中可以定义抽象和非抽象的属性和方法。

    特质叠加引发两种冲突

    第一种:一个类(Sub)混入的两个 trait(TraitATraitB)中具有相同的具体方法,且两个 trait 之间没有任何关系

    在这里插入图片描述
    如果当前父类和特质里面出现了相同的属性和方法,就会起冲突,必须在当前实现的子类(sub)中进行重写。

    举个栗子:

    class Person {
        val name: String = "Person"
        var age: Int = 18
    
        def sayHi(): Unit = {
            println(s"hello from : $name")
        }
    }
    
    trait Young {
        // abstract and non-abstract attribute
        var age: Int
        val name: String = "young"
    
        // method
        def play(): Unit = {
            println(s"young people $name is playing")
        }
        def dating(): Unit
    }
    
    trait Knowledge {
        var amount: Int = 0
        def increase(): Unit = {
            amount += 1
        }
    }
    
    trait Talent {
        def increase(): Unit = {
            println("increase talent")
        }
    }
    
    class Student extends Person with Young with Knowledge with Talent{
        override val name: String = "alice"
    
        def dating(): Unit = {
            println(s"Sutdent $name $age is dating")
        }
    
        def study(): Unit = println(s"Student $name is studying")
    
        override def sayHi(): Unit = {
            super.sayHi()
            println(s"hello from : student $name")
        }
    
        override def increase(): Unit = {
            super.increase() 
            println(s"studnet $name knowledge increase: $amount")
        }
    }
    
    object Trait {
        def main(args: Array[String]): Unit = {
            val s = new Student()
            s.sayHi()
            s.increase()
    
            s.study()
            s.increase()
    
            s.play()
            s.increase()
    
            s.dating()
            s.increase()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    第二种:一个类(Sub)混入的两个 trait(TraitATraitB)中具有相同的具体方法,且两个 trait 继承自相同的 trait(TraitC

    在这里插入图片描述
    Scala采用了特质叠加的策略来解决这类冲突。

    举个栗子:

    trait Ball {
      def describe(): String = "ball"
    }
    
    trait ColorBall extends Ball {
      val colorBall: String = "red"
    
      override def describe(): String = {
         colorBall+"-----"+super.describe()
      }
    }
    
    trait CategoryBall extends Ball{
      val categoryBall: String = "foot"
    
      override def describe(): String = categoryBall+"-----"+super.describe()
    }
    
    // 叠加顺序 MyFootBall -> ColorBall -> CategoryBall -> Ball
    class MyFootBall extends CategoryBall with ColorBall {
      override def describe(): String = "my ball is a" + super.describe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的super.describe()调用的实际上是排好序后的下一个特质中的 describe()方法。
    • 要调用某个指定的混入特质中的方法,可以增加约束:super[]
    	super[CategoryBall].describe()
    
    • 1

    自身类型

    • 自身类型可实现依赖注入的功能。
    • 一个类或者特征指定了自身类型的话,它的对象和子类对象就会拥有这个自身类型中的所有属性和方法。
    • 是将一个类或者特征插入到另一个类或者特征中,属性和方法都就像直接复制插入过来一样,能直接使用。但不是继承,不能用多态。

    举个栗子:

    object Test16_TraitSelfType {
      def main(args: Array[String]): Unit = {
        val user = new RegisterUser("alice", "1212121")
        user.insert()
      }
    }
    
    // 用户类
    class User(val name: String,val password: String){
    
    }
    
    trait UserDao{
      // 定义自身类型
      _: User =>
      // 注册用户
      def insert():Unit={
        println (s" insert into db: = ${this.name} , ${this.password}")
      }
    }
    
     // 定义注册用户
    class RegisterUser(name: String,password: String) extends User(name,password) with UserDao
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • _: SelfType =>,其中_的位置是别名定义,也可以是其他,_指代this。插入后就可以用this.xxx来访问自身类型中的属性和方法了。

    抽象类和特质的区别?

    优先使用特质。一个类可以扩展多个特质,但是只能扩展一个抽象类。
    需要构造函数参数,使用抽象类,抽象类可以定义带参的构造器,特质只是无参的构造器。

    四、扩展内容

    类型检查和转换

    • obj.isInstanceOf[T]:判断 obj 是不是 T 类型。
    • obj.asInstanceOf[T]:将 obj 强转成 T 类型
    • classOf 获取对象的类名。结果是class package.xxx.className
    • 获取对象的类:obj.getClass

    枚举类

    • 需要继承 Enumeration
    • Value类型定义枚举值。
    // 定义枚举类
    object WorkDay extends Enumeration{
      val MONDAY = Value(1,"星期一")
      val TUESDAY = Value(2,"星期二")
    }
    
    	println(WorkDay.MONDAY)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    应用类

    • 继承App,包装了main方法,就不需要显式定义main方法了,可以直接执行。
    object TestApp extends App{
      println("app start"
    }
    
    • 1
    • 2
    • 3

    定义新类型

    • 使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名
    • type SelfDefineType = TargetType
     type MyString= String
      val a: MyString = "abc"
      println(a)
    
    • 1
    • 2
    • 3

    本次分享的内容到这里就结束了,希望对大家学习Scala语言有所帮助!!!

    在这里插入图片描述

  • 相关阅读:
    Linux权限及Xshell运行原理
    域名服务:域名迁移
    快速绘制流程图「GitHub 热点速览 v.22.47」
    Idea插件整理
    【TiDB】TiCDC canal_json的实际应用
    为什么都去卷文本生成图像???
    DevOps全面综述:从概念到实践
    Vue状态管理 Storage Vuex Pinia
    设计模式之观察者模式
    【仿真建模-anylogic】动态生成ConveyorCustomStation
  • 原文地址:https://blog.csdn.net/Zp_insist/article/details/125569721