学习链接:https://www.bilibili.com/video/BV1Xh411S7bP?p=70&spm_id_from=pageDriver


1 Scala包

  1. 基本语法 package 包名
  2. Scala包的三大作用(和Java一样)

    1. 区分相同名字的类
    2. 当类很多时,可以很好的管理类
    3. 控制访问范围

      1.1 包的命名

      命名规则:只能包含数字、字母、下划线、小圆点,但不能用数字开头,也不要使用关键字

      1.2 包说明(包语句)

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

    import com.aaa.scala.Inner

    // 在外层包中定义单例对象 object Outer { var out: String = “out”

    def main(args: Array[String]): Unit = { println(Inner.in) // 外层不能直接访问内层,除非导包 } } package aaa { package scala { // 内层包中定义单例对象 object Inner {

    1. var in: String = "in"
    2. def main(args: Array[String]): Unit = {
    3. println(Outer.out) // out
    4. Outer.out = "outer"
    5. println(Outer.out) // outer
    6. }

    } }

    }

}

// 在同一文件中定义多个包 package bbb { package ccc {

  1. import com.aaa.scala.Inner
  2. object Test01_Package{
  3. def main(args: Array[String]): Unit = {
  4. println(Inner.in)
  5. }
  6. }

}

}

  1. 第二种风格有以下特点:
  2. - 一个源文件中可以声明多个package
  3. - 子包中的类可以直接访问父包中的内容,无需导包
  4. <a name="usksm"></a>
  5. ## 1.3 包对象
  6. Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有classobject的共享变量,可以被直接访问。<br />![QQ截图20220531204942.png](https://cdn.nlark.com/yuque/0/2022/png/26273875/1654049500617-1a1ccb0f-328b-46b2-93b2-f110d54467e9.png#clientId=u3a9961a8-16fa-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=uc1f4a67d&margin=%5Bobject%20Object%5D&name=QQ%E6%88%AA%E5%9B%BE20220531204942.png&originHeight=155&originWidth=754&originalType=binary&ratio=1&rotation=0&showTitle=false&size=12903&status=done&style=none&taskId=u68478525-b054-4ed3-b054-f963d686231&title=)
  7. ```scala
  8. package object chapter06 {
  9. // 定义当前包共享的属性和方法
  10. val commonValue = "aaa"
  11. def commonMethod() ={
  12. println(s"${commonValue}")
  13. }
  14. }
  • 采用嵌套方式管理包,包对象可与包定义在同一文件中,但是要保证包对象与包声明在同一作用域中 ```scala // 用嵌套风格定义包 package com {

    import com.aaa.scala.Inner

    // 在外层包中定义单例对象 object Outer { var out: String = “out”

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

    1. println(Inner.in) // 外层不能直接访问内层,除非导包

    } } package aaa { package scala {

    1. // 内层包中定义单例对象
    2. object Inner {
    3. var in: String = "in"
    4. def main(args: Array[String]): Unit = {
    5. println(Outer.out) // out
    6. Outer.out = "outer"
    7. println(Outer.out) // outer
    8. }
    9. }

    }

    }

}

// 在同一文件中定义多个包 package bbb { package ccc {

  1. import com.aaa.scala.Inner
  2. object Test01_Package{
  3. def main(args: Array[String]): Unit = {
  4. println(Inner.in)
  5. }
  6. }

}

}

  1. ```scala
  2. object Test02_PackageObject {
  3. def main(args: Array[String]): Unit = {
  4. commonMethod()
  5. println(commonValue)
  6. }
  7. }
  8. package chapter06 {
  9. object Test02_PackageObject {
  10. def main(args: Array[String]): Unit = {
  11. commonMethod()
  12. println(commonValue)
  13. }
  14. }
  15. }
  16. package ccc{
  17. package ddd{
  18. object Test02_PackageObject{
  19. def main(args: Array[String]): Unit = {
  20. println(school)
  21. }
  22. }
  23. }
  24. // 定义一个包对象 在同一层级下
  25. package object ddd{
  26. val school: String = "xxschool"
  27. }
  28. }
  • 导包

    • 可以在顶部使用import导入,在这个文件中的所有类都可以使用
    • 局部导入:什么时候使用,什么时候导入,在其作用范围内都可以使用
    • 通配符导入:import java.util._
    • 给类起名:import java.util.{ArrayList=>JL}
    • 导入相同包的多个类:import java.util.{HashSet,ArrayList}
    • 屏蔽类:import java.util.{ArrayList=>,} 导入了util中除ArrayList以外的所有
    • 导入包的绝对路径:new root.java.util.HashMap
      1. package java {
      2. package util {
      3. class HashMap {
      4. }
      5. }
      6. }
  • Scala中的三个默认导入分别是:

    • import java.lang._
    • import scala._
    • import scala.Predef._

      2 类和对象

      类:可以看成一个模板
      对象:表示具体的事物

      2.1 定义类

      Scala中没有public,一个.scala中可以写多个类
      基本语法:
      [修饰符] class 类名 {
      类体
      }
  • Scala语法中,类并不声明为public,所有这些类都具有共有可见性(即默认就是public)

  • 一个Scala源文件可以包含多个类 ```scala import scala.beans.BeanProperty

object Test03_Class { def main(args: Array[String]): Unit = { // 创建一个对象 val student = new Student() // student.name 私有属性,访问不到 println(student.sex) student.sex = “famale” } }

// 定义一个类 默认是public,但加上public会报错 class Student { // 定义属性 private var name: String = “aaa” @BeanProperty // 创建符合Java规范的getter setter val age: Int = 18 var sex: String = _ // 表示初值为空 }

  1. <a name="nT340"></a>
  2. ## 2.2 属性
  3. 属性是类的一个组成部分<br />**基本语法:**<br />[修饰符] var|val 属性名称 [:类型] = 属性值<br />Bean属性(@BeanProperty),可以自动生成规范的setXxx/getXxx方法
  4. ```scala
  5. class Student {
  6. // 定义属性
  7. private var name: String = "aaa"
  8. @BeanProperty // 创建符合Java规范的getter setter
  9. val age: Int = 18
  10. var sex: String = _ // 表示初值为空
  11. }

3 封装

Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。
Scala 并不推荐将属性设为 private ,再为其设置public 的 get 和 set 方法的做法。但由于很多Java框架都利用反射调用 getXXX和setXXX方法,有时候为了和这些框架兼容,也会为 Scala 的属性设置 getXXX 和 setXXX 方法(通过@BeanProperty 注解实现)。

3.1 访问权限

Scala中属性和方法的默认访问权限public,但Scala中无public关键字
private为私有权限,只在类的内部和伴生对象中可用
protected为受保护权限,Scala中受保护权限同类、子类可访问,同包无法访问
private[包名]增加包访问权限,包名下的其他类也可以使用

object Test04_ClassForAccess {

}

// 定义一个父类
class Person {
  private var idCard: String = "123456"
  protected var name: String = "aaa"
  var sex: String = "female"
  private[chapter06] var age: Int = 18

  def printInfo() = {
    println(s"Person: $idCard $name $sex $age")
  }
}
object Test04_Access {
  def main(args: Array[String]): Unit = {
    // 创建对象
    val person: Person = new Person()
    // person.idCard 私有属性无法访问
    // person.name protected在当前类和子类能被访问到
    println(person.age)
    println(person.sex)
    person.printInfo()
  }
}

// 定义一个子类
class Worker extends Person {
  override def printInfo(): Unit = {
    //    println(idCard) // error ,是私有的属性,只能在类内部或伴生对象访问
    name = "bob"
    age = 25
    sex = "male"
    println(s"Worker: $name $sex $age")
  }
}

3.2 构造器

Scala构造对象也需要调用构造方法,可以有任意多个构造方法
Scala类的构造器包括:主构造器和辅助构造器
基本语法:

class 类名(形参列表) { // 主构造器
  // 类体
  def this(形参列表) { // 辅助构造器
  }
  def this(形参列表) { // 辅助构造器可以有多个
  }
}
  • 辅助构造器,函数的名称this,可以有国歌,编译器通过参数的个数及类型来区分
  • 辅助构造方法不能直接构建对象,必须直接或间接调用主构造方法
  • 构造器调用其他另外的构造器,要求被调用构造器必须提前声明 ```scala object Test05_Constructor { def main(args: Array[String]): Unit = { val student1 = new Student1 // ()可以省略 student1.student1() /*

    1. 主构造方法被调用 不是构造方法,是一般方法 */

      val student2 = new Student1(“aaa”) /*

    2. 主构造方法被调用
    3. 辅助构造方法一被调用 name: aaa age: 0 */

      val student3 = new Student1(“bbb”, 25) /*

    4. 主构造方法被调用
    5. 辅助构造方法一被调用 name: bbb age: 0
    6. 辅助构造方法二被调用 name: bbb age: 25 */ } }

// 定义一个类 //class Student1 { // //} // 这样写表示主构造器没有参数,省略了空括号

class Student1() { // 定义属性 var name: String = var age: Int =

println(“1. 主构造方法被调用”)

// 声明辅助构造方法 def this(name: String) { this() // 直接调用主构造器 println(“2. 辅助构造方法一被调用”) this.name = name println(s”name: $name age: $age” ) }

def this(name: String, age: Int) { this(name) println(“3. 辅助构造方法二被调用”) this.age = age println(s”name: $name age: $age” ) }

def student1(): Unit = { println(“不是构造方法,是一般方法”) } }

<a name="YlNlq"></a>
## 3.3 构造器参数
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰

- 未用任何修饰符修饰,这个参数就是一个局部变量
- var修饰参数,作为类的成员属性使用,可以修改
- val修饰参数,作为类只读属性使用,不能修改
```scala
object Test06_ConstructorParams {
  def main(args: Array[String]): Unit = {
    val student2 = new Student2
    println(s"student2: name = ${student2.name}, age = ${student2.age}") // student2: name = null, age = 0
    student2.name = "aaa"
    student2.age = 18
    println(s"student2: name = ${student2.name}, age = ${student2.age}") // student2: name = aaa, age = 18

    val student3 = new Student3("bbb", 20)
    println(s"student3: name = ${student3.name}, age = ${student3.age}") // student3: name = bbb, age = 20

    val student5 = new Student5("bbb", 20)
    println(s"student5: name = ${student5.name}, age = ${student5.age}")
//    student5.age = 26 常量 不能更改

    val student6 = new Student6("ccc", 26, "xxxSchool")
    println(s"student6: name = ${student6.name}, age = ${student6.age}")
    student6.printInfo() // student6: name = ccc, age = 26, school = xxxSchool
  }
}

4 继承和多态

基本语法:

class 子类名 extends 父类名 { 类体 }
  • 子类继承父类的属性和方法
  • Scala是单继承 ```scala object Test07_Inherit { def main(args: Array[String]): Unit = { val student1 = new Student7(“aaa”, 18) /*

    1. 父类的主构造器调用
    2. 子类的主构造器调用 */

      val student2 = new Student7(“bbb”, 20, “std001”) /*

    3. 父类的主构造器调用
    4. 子类的主构造器调用
    5. 子类的辅助构造器调用 */ } }

// 定义一个父类 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”Student: $name $age $stdNo”) } }

**动态绑定**
```scala
object Test07_Inherit {
  def main(args: Array[String]): Unit = {
    val student1 = new Student7("aaa", 18)
    /*
    1. 父类的主构造器调用
    3. 子类的主构造器调用
     */

    val student2 = new Student7("bbb", 20, "std001")
    /*
    1. 父类的主构造器调用
    3. 子类的主构造器调用
    4. 子类的辅助构造器调用
     */

    val teacher = new Teacher
    teacher.printInfo()
    /*
    1. 父类的主构造器调用
    Teacher
     */

    def personInfo(person: Person7): Unit = {
      person.printInfo()
    }

    println("======================")
    val person = new Person7
    personInfo(student1)
    personInfo(teacher)
    personInfo(person)
  }
}

// 定义一个父类
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"Student: $name $age $stdNo")
  }
}

class Teacher extends Person7 {
  override def printInfo(): Unit = {
    println(s"Teacher")
  }
}

QQ截图20220602103350.png
动态绑定:Scala中属性和方法都是动态绑定,Java中只有方法是动态绑定

object Test08_DynamicBind {
  def main(args: Array[String]): Unit = {
    val student: Person8 = new Student8
    // Scala属性和方法都是动态绑定的
    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 = "student"

  override def hello(): Unit = {
    println("hello student")
  }
}

QQ截图20220602105432.png
对比Java的动态绑定,属性静态绑定,方法动态绑定

public class TestDynamicBind {
    public static void main(String[] args) {
        Worker worker = new Worker();
        System.out.println(worker.name);
        worker.hello();
        worker.hi();

        // 多态
        Person person = new Worker();
        System.out.println(person.name); // 静态绑定属性 person
        person.hello(); // 动态绑定方法 hello worker
        //        person.hi();
    }
}

class Person {
    String name = "person";
    public void hello() {
        System.out.println("hello person");
    }
}

class Worker extends Person {
    String name = "worker";
    public void hello() {
        System.out.println("hello worker");
    }
    public void hi() {

    }
}

5 抽象类

5.1 抽象属性和抽象方法

5.1.1 基本语法:

  • 定义抽象类:abstract class Person{} //通过abstract关键字标记抽象类
  • 定义抽象属性:val|var name: String // 一个属性没有初始化,就是抽象属性
  • 定义抽象方法: def hello(): String // 只声明而没有实现的方法,就是抽象方法

    类中只要存在抽象属性和抽象方法,就要定义成抽象类

5.1.2 继承&重写

  • 如果父类为抽象类,子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
  • 重写非抽象方法需要override修饰,重写抽象方法可以不加override
  • 子类中调用父类的方法使用super关键字
  • 子类对抽象属性进行实现,父类抽象属性可以用var修饰

子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var
(var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写)

object Test09_AbstractClass {
  def main(args: Array[String]): Unit = {
    val student = new Student9
    student.eat()
    student.sleep()
  }
}

// 定义抽象类
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

  override def sleep(): Unit = { // override可以省略
    println("student sleep")
  } // override可以省略

  // 重写非抽象属性和方法
  override val name: String = "student"

  override def eat(): Unit = {
    super.eat()
    println("student eat")
  }
}

5.2 匿名子类

可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类

object Test10_AnnoymousClass {
  def main(args: Array[String]): Unit = {
    val person: Person10 = new Person10 {
      override var name: String = "aaa"

      override def eat(): Unit = println("person eat")
    }
    println(person.name)
    person.eat()
  }
}

// 定义抽象类
abstract class Person10 {
  var name: String
  def eat(): Unit
}

6 单例对象(伴生对象)

Scala没有静态的操作。为了和Java交互,用单例对象模拟类对象。若单例对象名与类名一致,称该单例对象是这个类的伴生对象,这个类的所有静态内容都可以放在它的伴生对象中声明。

6.1 单例对象语法

基本语法:

object Person {
  val school: String = "xxx"
}
  • 单例对象采用object关键字声明
  • 单例对象对应得类称为伴生类,伴生对象的名称应该和伴生类名一致
  • 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问 ```scala object Test11_Object { def main(args: Array[String]): Unit = { val student = new Student11(“aaa”, 8) // student: name = aaa , age = 8, school = xxxschool student.printInfo() } }

// 定义类 class Student11(val name: String, val age: Int) { def printInfo(): Unit = { println(s”student: name = $name , age = $age, school = ${Student11.school}”) // 这里的Student11不是类,是伴生对象 } }

// 伴生对象 object Student11 { val school: String = “xxxschool” }

**构造方法私有化**
```scala
object Test11_Object {
  def main(args: Array[String]): Unit = {
//    val student = new Student11("aaa", 8) // student: name = aaa , age = 8, school = xxxschool
//    student.printInfo()
    val student = Student11.newStudent("aaa", 18)
    student.printInfo()
  }
}

// 定义类
// 构造方式私有化
class Student11 private (val name: String, val age: Int) {
  def printInfo(): Unit = {
    println(s"student: name = $name , age = $age, school = ${Student11.school}") // 这里的Student11不是类,是伴生对象
  }
}

// 伴生对象
object Student11 {
  val school: String = "xxxschool"

  // 定义一个类的对象实例的创建方法
  def newStudent(name: String, age: Int): Student11 = new Student11(name, age)
}

6.2 apply方法

  • 通过伴生对象的apply方法,实现不使用new方法创建对象
  • 如果想让主构造器变成私有的,可以在()之前加上private
  • apply方法可以重载
  • Scala中 obj (arg 的语句实际是在调用该对象的 apply 方法,即 obj .apply(arg)。用

以统一面向对象编程和函数式编程的风格。

  • 当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的是伴生对象的apply方法 ```scala object Test11_Object { def main(args: Array[String]): Unit = { // val student = new Student11(“aaa”, 8) // student: name = aaa , age = 8, school = xxxschool // student.printInfo() val student = Student11.newStudent(“aaa”, 18) student.printInfo()

    val student1 = Student11.apply(“bbb”, 20) student1.printInfo()

    // 与上面的student1写法等效 val student2 = Student11(“bbb”, 21) student2.printInfo() } }

// 定义类 // 构造方式私有化 class Student11 private (val name: String, val age: Int) { def printInfo(): Unit = { println(s”student: name = $name , age = $age, school = ${Student11.school}”) // 这里的Student11不是类,是伴生对象 } }

// 伴生对象 object Student11 { val school: String = “xxxschool”

// 定义一个类的对象实例的创建方法 def newStudent(name: String, age: Int): Student11 = new Student11(name, age)

def apply(name: String, age: Int): Student11 = new Student11(name, age) }

**单例模式**
```scala
object Test12_Singleton {
  def main(args: Array[String]): Unit = {
    val student1 = Student12.getInstance()
    student1.printInfo()

    val student2 = Student12.getInstance()
    student1.printInfo()

    println(student1)
    println(student2)
  }
}

// 定义类
class Student12 private (val name: String, val age: Int) {
  def printInfo(): Unit = {
    println(s"student: name = $name , age = $age, school = ${Student11.school}") // 这里的Student11不是类,是伴生对象
  }
}

// 饿汉式单例设计模式
//object Student12 {
//  private val student: Student12 = new Student12("ccc", 22)
//  def getInstance(): Student12 = student
//}

// 懒汉式单例设计模式
object Student12 {
  private var student: Student12 = _
  def getInstance(): Student12 = {
    if (student == null) {
      // 如果没有对象实例的话,创建一个
      student = new Student12("ddd", 25)
    }
    student
  }
}

7 特质(Trait)

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

7.1 特质声明

基本语法

trait 特质名 {
  trait 主体
}

7.2 特质基本语法

一个类具有某种特质(特征),意味着这个类满足了这个特质(特征)的所有要素,在使用时,采用extends关键字,如果有多个特质或存在父类,需要采用with关键字连接。
基本语法:
没有父类:class 类名 extends 特质1 with 特质2 with 特质3…
有父类:class 类名 extends 父类 with 特质1 with 特质2…

  • 类和特质的关系:使用继承的关系
  • 当一个类去继承特质时,第一个连接词时extends,后面是with
  • 如果一个类在同时继承特质和父类时,把父类写在extends后 ```scala object Test13_Trait { def main(args: Array[String]): Unit = { val student = new Student13 student.sayHello() student.study() student.watch() student.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 is playing”) }

def watch(): Unit }

class Student13 extends Person13 with Young { // 重写冲突的属性 override val name: String = “student” // 实现抽象方法 override def watch(): Unit = println(s”student ${name} is watching”)

def study(): Unit = println(s”student ${name} is studying”)

// 重写父类方法 override def sayHello(): Unit = { super.sayHello() println(s”hello from student $name”) } }

![QQ截图20220602173405.png](https://cdn.nlark.com/yuque/0/2022/png/26273875/1654162453613-62496b93-c4c6-4393-a37f-9be7489cd972.png#clientId=u3a6c1c3b-6097-4&crop=0&crop=0&crop=1&crop=1&from=ui&height=123&id=uc104cb2c&margin=%5Bobject%20Object%5D&name=QQ%E6%88%AA%E5%9B%BE20220602173405.png&originHeight=157&originWidth=321&originalType=binary&ratio=1&rotation=0&showTitle=false&size=14520&status=done&style=none&taskId=u8f5033b9-6822-4c9f-9e9a-e1c362869eb&title=&width=250.99758911132812)<br />**特质混入**<br />一个类可以混入(mixin)多个特质<br />所有的Java接口都可以当作Scala的特质使用<br />动态混入-灵活扩展类的功能

   - 创建对象时混入trait,而不是类混入该trait
   - 如果混入的trait中有未实现的方法,需要实现
```scala
object Test14_TraitMixin {
  def main(args: Array[String]): Unit = {
    val student = new Student14
    student.study()
    student.increase()

    student.play()
    student.increase()
    student.watch()
    student.increase()
  }
}

// 定义一个特质
trait Knowledge {
  var amount: Int = 0
  def increase(): Unit // 抽象方法
}

class Student14 extends Person13 with Young with Knowledge {
  // 重写冲突的属性
  override val name: String = "student"
  // 实现抽象方法
  override def watch(): Unit = println(s"student ${name} is watching")

  def study(): Unit = println(s"student ${name} is studying")

  // 重写父类方法
  override def sayHello(): Unit = {
    super.sayHello()
    println(s"hello from student $name")
  }

  // 实现特质中的抽象方法
  override def increase(): Unit = {
    amount += 1
    println(s"student $name knowledge increased: $amount")
  }
}

QQ截图20220602183702.png

object Test14_TraitMixin {
  def main(args: Array[String]): Unit = {
    val student = new Student14
    student.study()
    student.increase()

    student.play()
    student.increase()
    student.watch()
    student.increase()

    println("=============")
    // 动态混入
    val studentWithTalent = new Student14 with Talent {
      override def dancing(): Unit = println("student is good at dancing")

      override def singing(): Unit = println("student is good at singing")
    }

    studentWithTalent.sayHello()
    studentWithTalent.play()
    studentWithTalent.study()
    studentWithTalent.dancing()
    studentWithTalent.singing()
  }
}

// 定义一个特质
trait Knowledge {
  var amount: Int = 0

  def increase(): Unit // 抽象方法
}

trait Talent {
  def singing(): Unit
  def dancing(): Unit
}

class Student14 extends Person13 with Young with Knowledge {
  // 重写冲突的属性
  override val name: String = "student"

  // 实现抽象方法
  override def watch(): Unit = println(s"student ${name} is watching")

  def study(): Unit = println(s"student ${name} is studying")

  // 重写父类方法
  override def sayHello(): Unit = {
    super.sayHello()
    println(s"hello from student $name")
  }

  // 实现特质中的抽象方法
  override def increase(): Unit = {
    amount += 1
    println(s"student $name knowledge increased: $amount")
  }
}

QQ截图20220602184530.png

7.3 特质叠加

若混入的特质中具有相同的方法,出现继承冲突:

  1. 一个类混入两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系

解决冲突:在类中重写冲突方法
【Scala笔记】6.面向对象 - 图5

object Test15_TraitOverlying {
  def main(args: Array[String]): Unit = {
    val student = new Student15
    student.increase()
  }
}

trait Knowledge15 {
  var amount: Int = 0

  def increase(): Unit = {
    println("knowledge increased")
  }
}

trait Talent15 {
  def singing(): Unit
  def dancing(): Unit
  def increase(): Unit = {
    println("talent increased")
  }
}

class Student15 extends Person13 with Talent15 with Knowledge15 {
  override def dancing(): Unit = println("dancing")

  override def singing(): Unit = println("singing")

  override def increase(): Unit = {
    super.increase()
  }
}

QQ截图20220603191024.png调用最后一个trait的increase()

  1. 一个类混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TratiC),即钻石问题

解决冲突:特质叠加
【Scala笔记】6.面向对象 - 图7

object Test_Trait {
  def main(args: Array[String]): Unit = {
    // 钻石问题特征叠加
    val myFootBall = new MyFootBall
    println(myFootBall.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()
}

QQ截图20220604204926.png
指定调哪个父类

// 定义一个自定义球的类
class MyFootBall extends CategoryBall with ColorBall {
  override def describe(): String = "my ball is a " + super[CategoryBall].describe()
}

QQ截图20220604205041.png

7.4 特质叠加执行顺序

当一个类混入多个特质的时候,Scala会对所有的特质及其父特质按照一定的顺序进行排序
【Scala笔记】6.面向对象 - 图10
类的定义:class MyBall extends Category with Color
叠加顺序:MyBall ->Color -> Category -> Ball

  1. 列出混入的第一个特质的继承关系,作为临时叠加顺序

Category -> Ball

  1. 列出混入的第二个特质的继承关系,并将该顺序叠加到临时顺序前边

Color -> Ball

  1. 将子类放在临时叠加顺序的第一个,得到最终叠加顺序

MyBall

7.5 特质和抽象类的区别

  1. 优先使用特质。一个类可以扩展多个特质,但却只能扩展一个抽象类
  2. 如果需要构造函数参数,使用抽象类。抽象类可以定义带参数的构造函数,特质不行

    7.6 特质自身类型

    自身类型可实现依赖注入的功能 ```scala object Test16_TraitSelfType { def main(args: Array[String]): Unit = { val user = new RegisterUser(“aaa”, “123456”) user.insert() } }

// 用户类 class User(val name: String, val password: String)

trait UserDao { _: User =>

def insert(): Unit = { println(s”insert into db: ${this.name}”) } }

// 定义注册用户类 class RegisterUser(name: String, password: String) extends User(name, password) with UserDao

![QQ截图20220604205908.png](https://cdn.nlark.com/yuque/0/2022/png/26273875/1654347554994-355ca07c-5c1b-4def-be66-1d9d2b334a54.png#clientId=u1463f35c-8378-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u25618744&margin=%5Bobject%20Object%5D&name=QQ%E6%88%AA%E5%9B%BE20220604205908.png&originHeight=33&originWidth=227&originalType=binary&ratio=1&rotation=0&showTitle=false&size=2853&status=done&style=none&taskId=u331de425-ff8f-4c74-8f09-2786095c653&title=)
<a name="xe2IO"></a>
# 8 扩展
<a name="ZOZOc"></a>
## 8.1 类型检查和转换

1. obj.isInstanceOf[T]:判断obj是不是T类型
1. obj.asInstanceOf[T]:将obj强转成T类型
1. classOf获取对象的类名
```scala
object Test17_Extends {
  def main(args: Array[String]): Unit = {
    // 1. 类型的检测和转换
    val student: Student17 = new Student17("aaa", 18)
    student.study()
    student.sayHi()
    val person: Person17 = new Student17("bbb", 20)
    person.sayHi()

    println("student is Student17: " + student.isInstanceOf[Student17]) // true
    println("student is Person17: " + student.isInstanceOf[Person17]) // true
    println("person is Person17: " + person.isInstanceOf[Person17]) // true
    println("person is Student17: " + person.isInstanceOf[Student17]) // true

    // 类型转换
    if (person.isInstanceOf[Student17]) {
      val newStudent = person.asInstanceOf[Student17]
      newStudent.study() // student study
    }

    println(classOf[Student17]) // class chapter06.Student17
  }
}

class Person17(name: String, age: Int) {
  def sayHi(): Unit = {
    println("hi from person " + name)
  }
}

class Student17(name: String, age: Int) extends Person17(name, age) {
  override def sayHi(): Unit = {
    println("hi from student " + name)
  }
  def study(): Unit = {
    println("student study")
  }
}

8.2 枚举类和应用类

枚举类:需要继承Enumeration
应用类:需要继承App

object Test17_Extends {
  def main(args: Array[String]): Unit = {
    println(WorkDay.MONDAY) // Monday
  }
}


// 定义枚举类对象
object WorkDay extends Enumeration {
  val MONDAY = Value(1, "Monday")
  val TUESDAY = Value(2, "Tuesday")
}

// 定义应用类对象
object TestApp extends App {
  println("app start")
}

继承App,可以直接运行
QQ截图20220604211544.png

8.3 Type定义新类型

使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名

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

    type S=String
    var v:S="abc"
    def test():S="xyz"
 } 
}