• Scala入门到精通(尚硅谷学习笔记)章节八——面向对象


    scala包的作用:
    (1)区分相同名字的类
    (2)当类很多时,可以很好的管理类
    (3)控制访问范围

    包的命名

    命名规则
    只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字。
    命名规范
    一般是小写字母+小圆点

    com.公司名.项目名.业务模块名
    
    • 1

    包的管理

    Scala有两种包的管理风格。一种方式是每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用“”进行分隔以表示包的层级关系,如com.heria.scala
    另一种风格,通过嵌套的风格表示层级关系

    import com.heria.scala.Inner
    package com{
      object Outer {
        var out:String="out"
        def main(args: Array[String]): Unit = {
          println(Inner)
        }
      }
      package heria{
        package scala{
          //内层包中定义单类对象
          object Inner{
            var in:String = "in"
            def main(args: Array[String]): Unit = {
              println(Outer.out)//out
              Outer.out="Outer"
              println(Outer.out)//Outer
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    该风格具有两个特点:
    1)一个源文件中可以声明多个package
    (2)子包中的类可以直接访问父包中的内容,而无需导包

    导入包

    import com.heria.scala引入com.heria包下scala ( class和object )
    import com.heria._引入com.heria下的所有成员
    import com.heria.scala_引入scala(object)下的所有成员
    import com.heria.{sca,la}引入com.heria下的sca和la
    import com.heria.{sca=>scala}引入com.heria下的sca并更名为scala
    import com.heria.{sca=>scala,_}引入com.heria下的所有成员,并将sca更名为scala
    import com.heria.{scala=>,}引入com.heria包下屏蔽scala类
    new_root_.java.util.HashMap引入Java的绝对路径

    包对象

    在scala中可以为每个包定义一个同名的包对象,包对象的名字需要和该包下的子包一样。在包对象中可以定义变量,方法。在包对象中定义的变量和方法,就可以在对应的包中的任何类中访问、使用。在底层这个包对象会生成两个类 package.classpackage$.class
    idea在一个包下可以直接创建package object文件
    在这里插入图片描述
    案例实操
    新建名为chapter08的包,新建package object文件,创建一个常量一个方法用于测试

    package object chapter08 {
      val commonValue:String = "heria"
      def commonMethod():Unit = {
        println("This is a test")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在同一个包下新建一个名为Test01的object,可以看到Test01可以直接调用属性和方法

    object Test01 {
      def main(args: Array[String]): Unit = {
        println(commonValue)//heria
        commonMethod()//This is a test
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:包对象申明时必须要和当前这个包的定义在同一层级下

    类和对象

    类是对象的抽象,可以看成一类事物的模板,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
    基本和语法

    [修饰符] class 类名{
    	类体
    }
    
    • 1
    • 2
    • 3

    (1) Scala语法中,类修饰符可以省略,默认为public
    (2)一个scala源文件可以包含多个类
    案例实操

    object Test01 {
      def main(args: Array[String]): Unit = {
       //创建一个对象
        val student = new Student()
        //student.name //error,不能访问private属性
        println(student.age)//0
        println(student.sex)//null
        student.sex="female"
        println(student.sex)//female
      }
    }
    //创建类
    class Student{
      private var name:String ="alice"
      @BeanProperty
      var age:Int= _
      var sex:String = _
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    封装

    封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
    java封装操作如下:(1)将属性进行私有化(2)提供一个公共的set方法,用于对属性赋值(3)提供一个公共的get方法,用于获取属性的值
    Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。因此Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)。

    访问权限

    (1) scala中属性和方法的默认访问权限为 public,但Scala中无public关键字。
    (2) private为私有权限,只在类的内部和伴生对象中可用。
    (3) protected 为受保护权限,Scala 中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
    (4) private[包名]增加包访问权限,包名下的其他类也可以使用
    案例实操
    在同一包下新建两个object,一个为Test04_access,一个为Test04_classForAccess
    Test04_classForAccess

    package chapter08
    object Test04_classForAccess {
    }
    //定义一个父类
    class Person{
      private var idCard:String = "3525" //私有
      protected var name:String="Alice"  //受保护
      var sex:String="female"
      private[chapter06] var age : Int = 18
      def printInfo(): Unit = {
        println(s"Person: $idCard $name $sex $age")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Test04_access

    package chapter08
    
    object Test04_access {
      def main(args: Array[String]): Unit ={
      	//创建对象
        val person: Person = new Person()
          //person.idCard    //error,私有属性只在自己类的内部和伴生对象中可用
          //person.name      //error,同类、子类可以访问,同包无法访问
          println(person.age)
        println(person.sex)
      }
    }
    class Worker extends Person{
      println("worker:")
      //println(idCard)   //私有属性只在自己类的内部和伴生对象中可用
      name="bob"
      age=25
      sex="male"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    构造器

    scala提供主构造器和任意数量的辅助构造器。
    基本语法

    class 类名(形参列表) { // 主构造器
    	// 类体1
    	def this(形参列表) { // 辅助构造器
    	}
    	def this(形参列表) { //辅助构造器可以有多个...
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意细节:
    1.主构造器
    (1)主构造器的声明直接放置于类名之后
    (2)主构造器会执行类定义中的所有语句,这里可以体会到 Scala 的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别
    (3)如果想让主构造器变成私有的,可以在()之前加上 private,这样用户只能通过辅助构造器来构造对象了
    Scala 构造器作用是完成对新对象的初始化,构造器没有返回值。
    2.辅助构造器
    (1)辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数及类型来区分。
    (2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。
    (3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。
    案例实操

    package chapter08
    
    object Test05_Constructor {
      def main(args: Array[String]): Unit = {
        val student1 = new Student()
        student1.Student()
        //主构造方法被调用
        //一般方法被调用
    
        val student2 = new Student("alice")
        //主构造方法被调用
        //辅助构造器一被调用
        //name:alice age: 0
    
        val student3 = new Student("alice",25)
        //主构造方法被调用
        //辅助构造器一被调用
        //name:alice age: 0
        //辅助构造方法二被调用
        //name:alice age: 25
      }
    }
    
    //定义一个类
    class Student{
      //定义属性
      var name:String= _
      var age:Int= _
      println("主构造方法被调用")
    
      //声明辅助构造方法
      //辅助构造器一
      def this(name:String){
        this()  //直接调用主构造器
        println("辅助构造器一被调用")
        this.name=name
        println(s"name:$name age: $age")
      }
      //辅助构造器二
      def this(name : String,age: Int) {
        this(name)
        println("辅助构造方法二被调用")
        this.age= age
        println(s"name:$name age: $age")
      }
    
      //一般方法
      def Student():Unit={
        println("一般方法被调用")
      }
    }
    
    
    • 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

    3.参数
    单独定义属性的形式

    object Test06_ConstructorParams {
      def main(args: Array[String]): Unit = {
        val student2 = new Student2
        println(s"student2:name=${student2.name},age=${student2.age}")
      }
    }
    
    //定义类
    //无参构造器
    class Student2{
      //单独定义属性
      var name:String = _
      var age:Int = _
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (1)如果参数使用 var 关键字声明,那么 Scala 会将参数作为类的成员属性使用,并会提供属性对应的 xxx()[类似 getter]/xxx_$eq()[类似 setter]方法,即这时的成员属性是私有的,但是可读写。
    传参构造器

    object Test06_ConstructorParams {
      def main(args: Array[String]): Unit = {
        val student3 = new Student3("bob",23)
        println(s"student3:name=${student3.name},age=${student3.age}")
      }
    }
    
    //定义类
    //传参构造器
    class Student3(var name:String,var age:Int)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)Scala 类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量,无法在主函数中直接打印,只能在构造器中
    提供printInfo方法

    object Test06_ConstructorParams {
      def main(args: Array[String]): Unit = {
        val student4 = new Student4("bob",23)
        student4.printInfo()
      }
    }
    
    //定义类
    //传参构造器
    class Student4(name:String,age:Int){
      def printInfo(){
        println(s"student4:name=${name},age=$age")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (3)构造器参数用val关键字声明,那么 Scala 会将参数作为类的私有的只读属性使用,无法再做更改

    继承和多态

    继承

    基本语法:class 子类名 extends 父类名 (类体)
    (1)子类继承父类的属性和方法
    (2) scala是单继承

    object Test07_inheritance {
      def main(args: Array[String]): Unit = {
        val student1 = new Student7("alice",18)
        //1.父类的主构造器调用
        //3.子类的主构造器调用
        println("--------------")
        val student2 = new Student7("alice",18,"std001")
        //1.父类的主构造器调用
        //3.子类的主构造器调用
        //4.子类的辅助构造器调用
      }
    }
    class Person7(){
      var name:String= _
      var age:Int = _
      println("1.父类的主构造器调用")
      def this(name:String,age:Int){
        this()
        println("2.父类的辅助构造器调用")
        this.name = name
        this.age = age
      }
      def printInfo():Unit = {
        println(s"Person:$name $age")
      }
    }
    class Student7(name:String,age:Int) extends Person7{
      var stdNo:String = _
      println("3.子类的主构造器调用")
      def this(name:String,age:Int,stdNo:String){
        this(name,age)
        println("4.子类的辅助构造器调用")
        this.stdNo=stdNo
      }
    
      override def printInfo(): Unit = {
        println(s"Person:$name $age $stdNo")
      }
    }
    
    • 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

    val student1 = new Student7("alice",18)调用了子类的主构造器,但前提是先调用父类的主构造器,因此顺序是1,3
    val student2 = new Student7("alice",18,"std001")调用了子类的辅助构造器,由于this(name,age),需要先调用子类的主构造器,才能打印4.子类的辅助构造器调用这句话,而调用子类的主构造器前要先调用父类的主构造器,因此顺序是1,3,4

    多态

    多态也叫"动态绑定",指在执行期间判断所引用对象的实际类型,根据其实际类型调用其相应的方法。(编译期间不绑定方法,而是在运行时判断是对象是哪个类的实例,在调用该类方法)
    跟java的多态的区别是:java静态绑定属性,动态绑定方法;scala的属性和方法全部实现动态绑定
    案例实操

    object Test08_DynamicBind {
      def main(args: Array[String]): Unit = {
        val student:Person8 = new Student8
        println(student.name)//student
        student.hello()//hello student
      }
    
    }
    class Person8{
      val  name:String = "person"
      def hello():Unit = {
        println("hello person")
      }
    }
    class Student8 extends Person8 {
      override val  name:String = "person"
      override def hello():Unit = {
        println("hello person")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    抽象类

    在Scala中一个被abstract 修饰的类,类中包含没有实现的成员,即对象没有初始值,定义的方法没有方法体,则该类就是抽象类。
    基本语法

    // 定义抽象类
    abstract class 抽象类名 {
    	// 定义抽象字段
    	val 抽象字段名:类型
    	// 定义抽象方法
    	def 方法名(参数:参数类型,参数:参数类型…):返回类型
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    继承和重写
    (1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明
    为抽象类
    (2)重写非抽象方法需要用override修饰。重写抽象方法则可以不加override。(3)子类中调用父类的方法使用super关键字
    (4)子类对抽象属性进行实现,父类抽象属性可以用var修饰;子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var。因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写

    object Test09_AbstractClass {
      def main(args: Array[String]): Unit = {
    
      }
    
    }
    //定义抽象类
    abstract class Person9{
      //非抽象属性
      val name:String = "Person"
      //抽象属性
      var age:Int
    
      //非抽象方法
      def eat():Unit={
        println("person eat")
      }
      //抽象方法
      def sleep():Unit
    }
    
    //定义具体实现的子类
    class Student9 extends Person9{
      var age:Int = 18
      def sleep():Unit={
        println("student sleep")
      }
    
      //重写非抽象属性和方法
      override def eat(): Unit = {
        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
    • 33

    单例对象

    Scala语言是完全面向对象的语言,所以并没有静态的操作(即在scala中没有静态的概念,没有static关键字)。但是为了能够和Java语言交互,产生了一种特殊的对象来模拟这类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
    语法

    object 伴生对象名称(必须跟伴生类名称一致){
    	//定义静态属性和方法
    }
    
    • 1
    • 2
    • 3

    说明
    (1))单例对象采用object关键字声明
    (2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
    (3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
    案例实操1
    还是Student类的例子,输出学生的姓名,年龄和学校。考虑到学校相同,是个静态属性学校不需要定义在类的属性里了,可以定义在伴生对象中。

    object Test11_Object {
      def main(args: Array[String]): Unit = {
        val student = new Student11("alice",18)
        student.printerInfo()
      }
    }
    //定义类
    class Student11(val name:String,age:Int){
      def printerInfo(): Unit ={
        println(s"student:name = $name,age = $age,school = ${Student11.school}")
      }
    }
    object Student11{
      val school:String = "school11"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    案例实操2
    在实际项目中,我们会采用工厂方法模式(FACTORY METHOD)编程。我们不希望直接创建类实例,可以采用将构造器私有化,要想调用方法,在伴生对象中新建工厂方法创建出来。

    object Test11_Object {
      def main(args: Array[String]): Unit = {
        //val student = new Student11("alice",18)
        val student = Student11.newStudent("alice",18)
        student.printerInfo()
      }
    }
    //定义类
    class Student11 private(val name:String,age:Int){
      def printerInfo(): Unit ={
        println(s"student:name = $name,age = $age,school = ${Student11.school}")
      }
    }
    object Student11{
      val school:String = "school11"
      //创建类的对象实例的创建方法
      def newStudent(name:String,age:Int):Student11 = new Student11(name,age)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    伴生对象中,也可以定义apply方法,在调用伴生对象的apply方法时可以省略apply

    object Test11_Object {
      def main(args: Array[String]): Unit = {
        //val student = new Student11("alice",18)
        val student = Student11("alice",18)
        student.printerInfo()
      }
    }
    //定义类
    class Student11 private(val name:String,age:Int){
      def printerInfo(): Unit ={
        println(s"student:name = $name,age = $age,school = ${Student11.school}")
      }
    }
    object Student11{
      val school:String = "school11"
      //创建类的对象实例的创建方法
      def apply(name:String,age:Int):Student11 = new Student11(name,age)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    单例设计模式
    一个类只能有一个实例,并提供对该实例的全局访问点。通俗地说,就是一个类只能创建一个对象,并且在程序的任何地方都能够访问到该对象。
    如下代码new出来的Student11对象实例全局只有一份,可以创建student1和student2,通过println( student1)println( student2)代码打印出的地址是相同的

    object Test11_Object {
      def main(args: Array[String]): Unit = {
        //val student = new Student11("alice",18)
        val student1 = Student11.getInstance
        student1.printerInfo()
        val student2 = Student11.getInstance
        student2.printerInfo()
        println( student1)
        println( student2)
      }
    }
    //定义类
    class Student11 private(val name:String,age:Int){
      def printerInfo(): Unit ={
        println(s"student:name = $name,age = $age,school = ${Student11.school}")
      }
    }
    
    //饿汉式
    //object Student11{
    //  val school:String = "heria"
    //  private val student:Student11 = new Student11("alice",18)
    //
    //  def getInstance:Student11 = student
    //}
    
    object Student11{
      private var student:Student11 = _
      def getInstance():Student11 = {
        if(student==null){
          student = new Student11("alice",18)
        }
        student
      }
    }
    
    • 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

    特质

    Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同
    的特质(特征)时,就可以将这个特质(特征)独立出来,采用关健字trait声明。
    Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可
    以混入(mixin)多个特质。这种感觉类似于Java中的抽象类。
    Scala 引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。
    基本语法

    trait 特质{
    	trait 主体
    }
    
    • 1
    • 2
    • 3

    一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,
    所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with
    关键字连接。
    说明
    (1)类和特质的关系:使用继承的关系。
    (2)当一个类去继承特质时,第一个连接词是extends,后面是with。
    (3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。

    //没有父类
    class 类名 extends 特质1 with 特质2 with特质3  ...
    //有父类
    class 类名 extends 父类 with 特质1 with 特质2 with特质3  ...
    
    • 1
    • 2
    • 3
    • 4

    案例实操

    object Test13_Trait {
      def main(args: Array[String]): Unit = {
        val student = new Student13
        student.sayHello()
    //    hello from:heria
    //    hello from :student heria
        student.study()//student heria is studying
        student.play()//young people can play
      }
    }
    class Person13{
      val name:String = "person"
      var age:Int = 18
      def sayHello():Unit={
        println("hello from:" +name)
      }
    }
    //定义特质
    trait Young{
      //声明抽象和非抽象属性
      var age:Int
      val name:String = "young"
    
      def play():Unit = {
        println("young people can play")
      }
      def dating():Unit
    }
    class Student13 extends Person13 with Young{
      //重写冲突的属性
      override val name: String ="heria"
    
      def dating():Unit = println(s"student $name is dating")
      def study():Unit = println(s"student $name is studying")
      //重写父类方法
      override def sayHello(): Unit = {
        super.sayHello()
        println(s"hello from :student $name")
      }
    }
    
    • 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

    特质混入

    还是以上一个学生的例子为例,我们不希望在定义某一个类时就把特质混入进去,而是在创建实例时将特质加入。在创建有Young特质的学生的时候,再用val student = new Student13 with Young{}将特质混入

    object Test13_Trait {
      def main(args: Array[String]): Unit = {
        val student = new Student13 with Young{
          //重写冲突的属性
          override val name: String ="heria"
    
          def dating():Unit = println(s"student $name is dating")
          def study():Unit = println(s"student $name is studying")
        }
    
        student.sayHello()
    //    hello from:heria
    //    hello from :student heria
        student.study()//student heria is studying
        student.play()//young people can play
      }
    }
    class Person13{
      val name:String = "person"
      var age:Int = 18
      def sayHello():Unit={
        println("hello from:" +name)
      }
    }
    //定义特质
    trait Young{
      //声明抽象和非抽象属性
      var age:Int
      val name:String = "young"
    
      def play():Unit = {
        println("young people can play")
      }
      def dating():Unit
    }
    class Student13 extends Person13{
    
      //重写父类方法
      override def sayHello(): Unit = {
        super.sayHello()
        println(s"hello from :student $name")
      }
    }
    
    • 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

    特质叠加

    由于一个类可以混入(mixin)多个trait,且trait 中可以有具体的属性和方法,若混入
    的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。
    冲突分为以下两种:
    第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。方法的重写需要super语句,子类在继承父类或者特质时,后一个将前一个同名的方法覆盖,最终super返回调用的是最后一个特质对应的方法。
    在这里插入图片描述
    第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait (TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。
    在这里插入图片描述
    案例实操

    object Test15_TraitOverLying {
      def main(args: Array[String]): Unit = {
        var ball = new MyFootBall
        println(ball.describe())
      }
    }
    //定义球类特征
    trait Ball{
      def describe():String = "ball"
    }
    //定义颜色特征
    trait ColorBall extends Ball{
      var color:String = "red"
      override def describe():String  = color + "-" +super.describe()
    }
    //定义种类特征
    trait CategoryBall extends Ball{
      var category:String = "foot"
      override def describe():String  = category + "-" +super.describe()
    }
    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
    • 23

    最终输出的是my ball is a red-foot-ball。当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的super.describe()调用的实际上是排好序后的下一个特质中的 describe()方法。class MyFootBall extends CategoryBall with ColorBall排序规则如下:
    1)列出混入的第一个特质(Category),作为临时叠加顺序
    2)列出混入的第二个特质(Color)的继承关系,并将该顺序叠加到临时顺序前面,已经出现的特质不再重复
    3)将子类(MyBall)放在临时叠加顺序的第一个,得到最终的叠加顺序MyClass-->Color-->Category-->Ball

  • 相关阅读:
    JMeter测试Web服务
    [附源码]Python计算机毕业设计毕业生就业信息管理系统
    Linux基础IO【II】
    阿里妈妈API接口;item_search - 按关键字或网址搜索商品
    实验6、白盒测试:路径测试及测试用例设计
    【CSS】头部尾部固定中间滚动
    阿里十年总结,这份【Spring架构深度解析】已经被各大厂拿来当面试题了
    (附源码)ssm某村青年人口信息管理系统 毕业设计 271621
    《计算机视觉中的多视图几何》笔记(7)
    SSM+便民自行车管理系统 毕业设计-附源码191633
  • 原文地址:https://blog.csdn.net/weixin_47726676/article/details/126275482