Scala面向对象【下】

news/2024/7/5 5:51:21

 1、特质

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

1.1、特质的声明

基本语法

trait 特质名 {
    trait 主体
}

重写冲突属性案例

  • 一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了 extends 关键字,如果有多个特质或存在父类,那么需要采用 with 关键字连接(相当于 Java 的 implements )。
  • 如果一个类既继承了一个父类又继承了一个特质,那么如果这个父类和特质又相同的属性,在访问该属性时就会产生冲突,子类需要重写该属性。
class Person13{
  val name: String = "person"
  val age: Int = 20

  def sayHello(): Unit ={
    println("hello " + name)
  }
}


//定义一个特质
trait Young {

  //声明一个非抽象属性
  val name: String = "young"

  //声明一个抽象方法
  def study(): Unit

  //声明一个非抽象方法
  def play(): Unit = {
    println("play")
  }

}

class Student13 extends Person13 with Young {

  //重写冲突属性
  override val name = "young student"


  //实现抽象方法
  def study(): Unit = {
    println(s"student ${name} like study")
  }


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

}

1.2、动态混入

动态混入,也就是在创建对象的时候再去继承特质实现抽象属性和方法。

object Test {

  def main(args: Array[String]): Unit = {
    //动态混入 创建对象时灵活扩展特质
    val student1 = new Student13 with sexTrait {
      //实现抽象属性 val 不可变更为 var
      val sex = "young student"
    }

    //调用混入的trait属性
    student1.sex
  }

}

class Person13{
  val name: String = "person"
  val age: Int = 20

  def sayHello(): Unit ={
    println("hello " + name)
  }
}

//声明一个特质
trait sexTrait {
  val sex: String
}

class Student13 extends Person13{

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

}

1.3、特质叠加

        由于一个类可以混入(mixin )多个 trait ,且 trait 中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:
  • 第一种,一个类(Sub)混入的两个 traitTraitATraitB)中具有相同的具体方法,且两个 trait 之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。

 

trait A{
  val num: Int
  def fun(): Unit = {
    println("A")
  }
}

trait B{
  val num: Int = 10
  def fun(): Unit = {
    println("B")
  }
}
class Student14 extends Person13 with A with B{

  //重写方法 fun
  override def fun(): Unit = {
    super.fun() //最后继承的特质是B 所以fun方法会调用B的fun方法
  }

}

object Test14_TraitOverlying {
  def main(args: Array[String]): Unit = {
    val stu = new Student14
    stu.fun() //输出 B
    println(stu.num)    //10
  }
}

特质叠加的顺序

        特质A和B中都含有变量num,但是特质A并没有初始化num的值,而特质B初始化了num的值。正因为最后继承了特质B,而特质B中实现了num变量的赋值,所以我们在子类中不需要实现或者重写num的值,可以直接访问num的值,这时候访问到的也就是特质B中初始化的值。

        如果A也初始化了num的值,则B无法叠加。

        如果A初始化了num的值,B没有,尽管最后继承的是特质B,但B仍然无法覆盖num,所以访问到的是A定义的num的值。

trait horse{
  val name: String = "马"
}
trait donkey{
  val name: String 
}

class Animal{

}

//骡子
class Mule extends Animal with horse with donkey {

}

object Test {

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

    val mule = new Mule
    println(mule.name)    //马

  }

}
trait horse{
  val name: String
}
trait donkey{
  val name: String = "驴"
}

class Animal{
}

//骡子
class Mule extends Animal with horse with donkey {
 
}

object Test {

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

    val mule = new Mule
    println(mule.name)    //驴

  }

}
  • 第二种,一个类(Sub)混入的两个 traitTraitATraitB)中具有相同的具体方法,且两个 trait 继承自相同的 traitTraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala 采用了特质叠加的策略

object Test {

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

    val mule = new Mule
    println(mule.describe())    //骡子是 驴-马-爬行动物


  }

}

trait horse extends crawler {
  val t1: String = "马-"

  override def describe(): String = t1 + super.describe()
}
trait donkey extends crawler {
  val t2: String = "驴-"

  override def describe(): String = t2 + super.describe()
}

//爬行动物特征
trait crawler {
  def describe(): String = {
    "爬行动物"
  }
}


//骡子
class Mule extends horse with donkey {
  override def describe(): String = "骡子是 " + super.describe()
}

特质叠加顺序:骡子 -> 驴 -> 马 -> 爬行动物 

指定父类的方法

        直接 super[父类].方法名,这样就不会去使用叠加后的方法,而是直接使用我们指定的子类的某个父类的方法,不用担心被叠加的问题

class Mule extends horse with donkey {
  override def describe(): String = "骡子是 " + super[horse].describe()
}
object Test {
  def main(args: Array[String]): Unit = {

    val mule = new Mule
    println(mule.describe())    //骡子是 马-爬行动物

  }
}

super关键字

        在Scala中,super关键字只能用于调用父类的方法,不能用于访问或调用父类的属性。这是因为在Scala中,属性访问器(gettersetter)实际上是方法的一种特殊形式。如果需要在子类中访问父类的属性,你可以在父类中定义一个公共的方法(getter方法)来返回属性的值。然后在子类中通过调用该方法来获取父类的属性值。

1.4、自身类型

自身类型实现了 依赖注入 的功能。

案例-用户注册

package chapter06

object Test16_SelfType {

  def main(args: Array[String]): Unit = {
    val user = new RegisterUser("tom","123456")
    user.register()
  }

}

class User(val name: String,val password: String)

trait UserDao{
  //把自身类型定义为User 相当于我们已经有一个User实例对象 直接在这操作
  _: User =>
  def register(): Unit = {
    val res =
       s"""
        |注册成功!
        |用户名: ${this.name}
        |密码:   ${this.password}
        |""".stripMargin

    println(res)
  }
}

class RegisterUser(name: String,password: String) extends User(name, password) with UserDao
注册成功!
用户名: tom
密码:   123456

1.5、扩展

类型的检查与转换

  1. obj.isInstanceOf[T]:判断 obj 是不是 T 类型。
  2. obj.asInstanceOf[T]:将 obj 强转成 T 类型。
  3. classOf: 获取对象的类名。

枚举和应用类

枚举类:需要继承 Enumeration
应用类:需要继承 App
object Test {
  def main(args: Array[String]): Unit = {

    println(WorkDay.MONDAY) //Monday
    println(WorkDay.TUESDAY) //Tuesday

  }
}

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

//定义应用类对象 不需要main方法 直接就可以运行
object TestApp extends App{
  println("app start")
}

Type 

使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名
 type Str = String
  val word: Str = "hello"
  print(word)


http://lihuaxi.xjx100.cn/news/1289246.html

相关文章

koa+docker部署笔记及技术问题总结

部署 koa 服务 一、应用介绍 1. 基础构成 centerOSdockerkoavue 2. 实施步骤: 安装 docker安装配置 node 镜像构建镜像运行项目测试部署是否成功 二、实施 1.安装配置 docker 介绍 Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0…

Spring Boot 中的事务超时时间

Spring Boot 中的事务超时时间 在 Spring Boot 中,事务管理是一个非常重要的话题。当我们在数据库中执行一些复杂的操作时,需要确保这些操作能够在一定的时间内完成,否则可能会导致数据一致性问题。为了解决这个问题,Spring Boot…

JMeter:如何开始简单的WEB压力测试?

目录 背景 如何开始简单的WEB压力测试 PutsReq网站截图 执行测试计划 背景 JMeter是一款广泛使用的性能测试工具,它可以模拟用户行为并生成负载,用于评估Web应用程序的性能和稳定性。 最近工作上被安排针对Web网站进行性能压测,以评估特…

从URL输入到页面展示过程

99. 从URL输入到页面展示过程 在日常的网页浏览中,我们只需要输入URL并按下回车键,就能迅速看到页面的展示。然而,背后涉及到了一系列复杂的步骤和技术。本文将详细介绍从URL输入到浏览器页面展示的完整过程,让我们更深入地了解浏…

【Spring Cloud Alibaba Sentinel 实现熔断与限流】 —— 每天一点小知识

💧 S p r i n g C l o u d A l i b a b a S e n t i n e l 实现熔断与限流 \color{#FF1493}{Spring Cloud Alibaba Sentinel 实现熔断与限流} SpringCloudAlibabaSentinel实现熔断与限流💧 🌷 仰望天空,妳我亦是行人.✨…

js内存泄漏及排查详解

js内存泄漏及排查详解 常见内存泄漏及解决方案 内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。 隐式全局变量 在局部…

v8-tc39-ecma262:concat,不只是合并数组

如上图,解释如下: 如果是对象o,转换为对象新建数组A设n0,用于最后赋值给A,确保A的长度正确预先把值设置到items(这里不知何意?)循环items,设置元素为E E是否可展开如果可展开 有len下标,则获取…

QT多线程之QtConcurrent::run()

QtConcurren导读 QtConcurrent提供了编写多线程程序的高级api,也即不使用低级线程原语,而其他实现多线程的方式,例如子类化QThread、QObject::moveToThread()、子类化QRunnable对于共享数据的保护都要使用低级线程原语,这无疑是要…