Scala详解(4)

server/2024/12/22 14:31:34/

Scala

面向对象

案例

  1. 定义类表示矩形(Rectangle),提供对外获取周长(girth)和面积(area)的函数,并且为这个矩形类提供一个子类表示正方形(Square)

    package com.fesco.exer
    ​
    object ObjectDemo1 {
    ​def main(args: Array[String]): Unit = {
    ​val r = new Rectangle(5.2, 8.5)println(r.girth)println(r.area)
    ​val s = new Square(4.8)println(s.girth)println(s.area)
    ​}
    ​
    }
    ​
    // 定义一个类表示矩形
    // 矩形肯定要包含两个属性:height和width
    // 矩形定义好之后,宽和高不能发生改变
    // 定义在构造器中的常量属性,允许在创建对象的时候给值
    class Rectangle(val height: Double, val width: Double) {
    ​def girth: Double = (height + width) * 2
    ​def area: Double = height * width
    ​
    }
    ​
    // 子类:正方形
    class Square(override val width: Double) extends Rectangle(width, width)

抽象类和抽象方法

  1. 在Scala中,依然是通过abstract来定义抽象类。不同的地方在于,Scala中不需要使用abstract来定义抽象方法;如果一个方法在定义的时候没有函数体,那么默认就是抽象方法,不需要写abstract

  2. Scala中,抽象类中还可以定义抽象属性!!!

  3. 案例

    // 定义类表示图形Shape
    // 凡是图形,都应该有获取面积和周长的方法
    // 问题在于,不同的图形,获取面积和周长的方式是不同的
    // 那么也就意味着,需要在Shape中提供函数来获取面积和周长
    // 但是函数不需要具体的实现,而是由不同的子类来具体实现
    abstract class Shape {
    ​// 抽象属性// 子类继承抽象类的时候来覆盖这个属性// 抽象属性,就意味着子类必须有这个属性,但是父类不确定这个属性的值val versionUID: Long
    ​// 周长def girth: Double
    ​// 面积def area: Double
    ​
    }

  4. 抽象类中可以包含抽象方法和实体方法

  5. 子类继承抽象类,就需要覆盖其中的抽象函数

    package com.fesco.abstractx
    ​
    object AbstractDemo1 {
    ​def main(args: Array[String]): Unit = {
    ​val s:Shape = new Circle(5)println(s.area)println(s.girth)
    ​}
    ​
    }
    ​
    // 定义类表示图形Shape
    // 凡是图形,都应该有获取面积和周长的方法
    // 问题在于,不同的图形,获取面积和周长的方式是不同的
    // 那么也就意味着,需要在Shape中提供函数来获取面积和周长
    // 但是函数不需要具体的实现,而是由不同的子类来具体实现
    abstract class Shape {
    ​// 抽象属性// 子类继承抽象类的时候来覆盖这个属性// 抽象属性,就意味着子类必须有这个属性,但是父类不确定这个属性的值val versionUID: Long
    ​// 周长def girth: Double
    ​// 面积def area: Double
    ​
    }
    ​
    // 圆形
    class Circle(val radius: Double) extends Shape {
    ​override val versionUID: Long = 4589512675L
    ​override def girth: Double = 2 * radius * Math.PI
    ​override def area: Double = Math.PI * radius * radius
    }

伴生对象(object)

  1. 在Java中,提供了static表示静态,可以通过类来调用静态属性或者静态方法,但是Scala作为一门完全面向对象的语言,认为静态是不合理的,因为静态和对象是无关的,Scala就认为static破坏了面对象对象的原则,因此Scala干脆不支持static

  2. 为了实现和static类似的效果,在Scala提供了伴生对象(object)。可以为每一个类提供一个同名的object(伴生对象),定义在伴生对象中的属性和函数,在编译之后会自动变成静态的。此时和object同名的类称之为伴生类

    // 伴生类
    class Person{}
    // 伴生对象
    object Person{}
  3. 伴生类和伴生对象必须同名,编译的时候才会编译到一个class文件中

    package com.fesco.objectx
    ​
    object CalcDemo {
    ​def main(args: Array[String]): Unit = {
    ​// add可以通过类名来调用println(Calculator.add(5, 8))// test必须通过对象来调用val c = new Calculatorc.test()
    ​}
    ​
    }
    ​
    object Calculator {
    ​def add(x: Int, y: Int): Int = x + y
    ​
    }
    ​
    class Calculator {
    ​def test(): Unit = println("running")
    ​
    }

  4. 案例:单例模式

    package com.fesco.objectx
    ​
    object SingletonDemo {
    ​def main(args: Array[String]): Unit = {
    ​val a = A.getInstanceprintln(a)val b = B.getInstanceprintln(b)
    ​}
    ​
    }
    ​
    // 单例模式:全局过程中只存在唯一实例
    // 不能对外创建对象 - 构造器私有化
    class A private()
    ​
    // 定义伴生对象
    object A {// 提供本类对象,一般不会直接对外操作,所以需要私有化// 饿汉式private val a = new A
    ​// 对外提供函数来获取这个对象def getInstance: A = a
    }
    ​
    class B private()
    ​
    object B {private var b: B = _
    ​// 懒汉式def getInstance: B = {if (b == null) b = new Bb}
    }

特质/特征(trait)

  1. 在Scala中,没有接口的说法,而是提供了类似的机制:trait

  2. 定义结构

    trait 特质名 {特质体
    }
  3. 不同于Java的地方在于,特质中可以有抽象方法和抽象属性,也可以有实体方法和实体属性

  4. 在Scala中,一个类可以混入(mixin)多个特质!所以,特质可以看作是对单继承的补充

  5. 注意:在Scala中,如果一个类本身没有继承父类,那么在混入一个特质的时候需要使用extends,混入其他特质的时候使用with

    package com.fesco.traitx
    ​
    object TraitDemo {
    ​def main(args: Array[String]): Unit = {
    ​val s = new Students.study()s.break()s.play()
    ​}
    ​
    }
    ​
    trait Study {// 抽象函数def study(): Unit
    }
    ​
    trait Break {def break(): Unit
    }
    ​
    trait Play {def play(): Unit
    }
    ​
    // 没有父类
    // 使用第一个特质的时候,需要使用extends关键字
    // 类混入特质之后,需要覆盖特质中的抽象方法
    // 从第二个特质开始,需要使用with来关联
    class Student extends Study with Break with Play {
    ​override def study(): Unit = println("学习中~~~")
    ​override def break(): Unit = println("休息中~~~")
    ​override def play(): Unit = println("玩耍中~~~")
    }

  6. 如果一个类本身已经有了父类,那么此时所有的特质只能使用with来混入

    package com.fesco.traitx
    ​
    object TraitDemo2 {
    ​def main(args: Array[String]): Unit = {
    ​val c = new Circle(5)println(c.girth)println(c.area)
    ​}
    ​
    }
    ​
    trait Girth {def girth: Double
    }
    ​
    trait Area {def area: Double
    }
    ​
    class Shape {val name: String = "Shape"
    }
    ​
    // 如果一个类本身已经有了父类,那么此时所有的特质只能使用with来混入
    class Circle(val r: Double) extends Shape with Girth with Area {
    ​override val name: String = "Circle"
    ​override def girth: Double = 2 * Math.PI * r
    ​override def area: Double = Math.PI * r * r
    }

  7. 需要注意的是,在Scala中,不只是类中可以混入特质,在声明对象的时候也可以单独的混入特质,这种方式称之为动态混入

    package com.fesco.traitx
    ​
    object TraitDemo3 {
    ​def main(args: Array[String]): Unit = {
    ​// Teacher对象 - 是否能够确定这个老师对应的科目// val t: Teacher = new Teacher// 就希望在定义对象的时候,能够顺便指定老师对应的科目val t1: Teacher with Chinese = new Teacher with Chinese {}println(t1.subject)val t2:Teacher with  Maths = new Teacher with Maths {}println(t2.subject)
    ​}
    ​
    }
    ​
    // 代表老师的类
    class Teacher
    ​
    // 语文课程
    trait Chinese {def subject: String = "语文"
    }
    ​
    // 数学课程
    trait Maths {def subject: String = "数学"
    }

  8. 正因为一个类中可以混入多个特质,且特质中还可以定义实体方法,所以在混入特质的时候就可能会产生冲突

    package com.fesco.traitx
    ​
    object TraitDemo4 {
    ​def main(args: Array[String]): Unit = {
    ​val d = new Dprintln(d.m(5))val e = new Eprintln(e.m(5))val f = new Fprintln(f.m(5))
    ​}
    }
    ​
    trait A {def m(x: Int): Int = x
    }
    ​
    trait B {def m(x: Int): Int = x * 2
    }
    ​
    trait C {def m(x: Int): Int = x * 3
    }
    ​
    // D混入特质A、B、C
    // 如果不指定,编译的时候会从右到左来依次寻找这个函数,只要能够找到,编译就不报错
    // 此时建议在类中重写这个函数
    class D extends A with B with C {override def m(x: Int): Int = x * 4
    }
    ​
    class E extends A with B with C {// 需要使用B特质的逻辑// 调用特质B中的m函数override def m(x: Int): Int = super[B].m(x)
    }
    ​
    // 不指定,那么会先使用C中的特质 --- 从右到左
    class F extends A with B with C {override def m(x: Int): Int = super.m(x)
    }

  9. 注意:如果混入了多个特质,且特质中有同名函数,但是函数的返回值类型不同,那么此时混入后会报错

    package com.fesco.traitx
    ​
    object TraitDemo5 {
    ​def main(args: Array[String]): Unit = {
    ​val t = new T3t.m(5)
    ​}
    ​
    }
    ​
    trait T1 {def m(x: Int): Int = x
    }
    ​
    trait T2 {def m(x: Int): Unit = println(x)
    }
    ​
    class T3 extends T1 with T2 {// 此时返回值无论是Unit还是Int都会报错// override def m(x: Int): Int = x * 2
    }

  10. 因此,在混入特质的时候,需要检查特质中的同名同参数列表的函数的返回值类型是否一致

  11. 在Java中,接口之间是可以通过extends来基础,同样,Scala中,trait之间也可以通过extends来继承

    trait A
    trait B extends A
  12. 正因为特质之间允许被继承,所以存在菱形继承的问题

    package com.fesco.traitx
    ​
    object TraitDemo6 {
    ​def main(args: Array[String]): Unit = {
    ​val s = new SubAprintln(s.m(5))
    ​}
    ​
    }
    ​
    trait SuperA {def m(x: Int): Int = x
    }
    ​
    // SubA1继承了特质SuperA
    trait SubA1 extends SuperA {override def m(x: Int): Int = x * 2
    }
    ​
    // SubA2继承了特质SuperA
    trait SubA2 extends SuperA {override def m(x: Int): Int = x * 3
    }
    ​
    // 由于SubA1和SubA2有共同的父特质,所以构成了菱形继承关系
    // 此时由于子特质SubA1和SubA2中重写了函数,没有调用父特质中的函数
    // 所以调用的时候是从右到左来寻找这个函数
    class SubA extends SubA1 with SubA2

  13. 菱形继承中的特质叠加

    package com.fesco.traitx
    ​
    object TraitDemo7 {
    ​def main(args: Array[String]): Unit = {
    ​val s = new SubBprintln(s.repeat("abc"))
    ​}
    ​
    }
    ​
    trait SuperB {
    ​def repeat(str: String): String = str
    ​
    }
    ​
    trait Sub1 extends SuperB {
    ​override def repeat(str: String): String = {println("Sub1 running ~~~")super.repeat(str) * 3}
    ​
    }
    ​
    trait Sub2 extends SuperB {
    ​override def repeat(str: String): String = {println("Sub2 running ~~~")super.repeat(str) + str}
    }
    ​
    // 如果混入了多个特质,且混入的特质之间有公共父特质,那么此时就会形成"特质叠加"现象:
    // 从右到左,Sub2中的super不是SuperB,而是Sub1,Sub1中的super才是SuperB
    class SubB extends Sub1 with Sub2

其他

自身类型(self-type)
  1. 自身类型,指的是在另一个类或者特质中指定一个其他类型,那么这个类或者这个特质的子类或者子特质就需要满足指定类型

    class 类名 {别名:类型 =>函数
    }
    // 或者
    trait 特质名 {别名:类型 =>函数
    }
  2. 案例

    package com.fesco.otherobject SelfTypeDemo {def main(args: Array[String]): Unit = {val r = new Register("Bob", "abc12343", "13547894512")r.register()}}// 代表用户的类
    class User(val username: String, val password: String, val phoneNumber: String)// 产生用户数据之后,需要将数据放入数据库中
    // 这个类负责和数据库来进行交互
    trait UserDao {// UserDao的子类或者子特质需要满足使用User类的要求// 凡是本类中使用到User的地方,都可以用u来表示User对象// u:User =>// 如果需要将User类和本类结合this: User =>def insert(): Unit = println(s"username:${this.username}, password:${this.password}, phone number:${this.phoneNumber}")
    }// 表示注册的类
    class Register(override val username: String, override val password: String, override val phoneNumber: String) extends User(username, password, phoneNumber) with UserDao {def register(): Unit = super.insert()
    }

APP类
  1. APP类是Scala中提供的一个特殊的特质。当一个类混入了APP之后,那么这个类就变成了一个快速启动类,利用这个特质,可以省略主函数的书写

    package com.fesco.otherobject APPDemo extends App {println("Hello world")}
type
  1. 在Scala中,可以通过type关键字来定义数据类型,其实就是给这个类起别名

    package com.fesco.otherobject TypeDemo {def main(args: Array[String]): Unit = {val d1: Double = 3.2println(d1)// 起别名// Double类型的别名就是Dtype D = Doubleval d2: D = 5.87println(d2)}}

枚举类
  1. 当一个类中有多个固定个数的实例,并且可以一一列举的时候 ,可以定义为枚举。例如:季节、星期、月份等

  2. 案例

    package com.fesco.otherobject EnumDemo {def main(args: Array[String]): Unit = {val w = Week.MONDAYprintln(w)// 获取这个类中的所有的枚举常量,然后放入一个Set集合中返回val values: Week.ValueSet = Week.valuesfor (elem <- values) {println(elem)}}}// 定义一个枚举表示星期
    // 在Scala中,没有enum关键字,而是通过object来定义枚举
    // 在Scala中,所有的枚举都有一个顶级父类:Enumeration
    object Week extends Enumeration {// val MONDAY = Value(1)// val MONDAY = Value("Monday")// 实际过程中,习惯上枚举常量都是大写的val MONDAY: Week.Value = Value(1, "Monday")val TUESDAY: Week.Value = Value(2, "Tuesday")val WEDNESDAY: Week.Value = Value(3, "Wednesday")val THURSDAY: Week.Value = Value(4, "Thursday")val FRIDAY: Week.Value = Value(5, "Friday")val SATURDAY: Week.Value = Value(6, "Saturday")val SUNDAY: Week.Value = Value(7, "Sunday")}

密封类(sealed)
  1. sealed是Scala中提供的一个特殊的关键字,可以用于修饰类或者特质。如果sealed修饰类或者特质,形成如下限制

    1. 被修饰的类或者特质的子类或者子特质必须必须和当前类处在同一个scala文件中,不能跨文件

    2. 被修饰的类或者特质参与模式匹配的时候,那么会进行检查,如果没有罗列所有的情况,那么会警告

  2. 案例

    package com.fesco.other
    
    
    object SealedDemo {
    }sealed trait Student {def study(): Unit
    }class Pupil extends Student {override def study(): Unit = println("小学生学习中")
    }

  3. sealed增强了封装特性


http://www.ppmy.cn/server/8981.html

相关文章

SpringSecurity集成JWT

使用 Spring Security 集成 JWT&#xff08;JSON Web Token&#xff09;身份验证是一种常见的方式来实现基于令牌的身份验证。在 Spring Boot 应用程序中使用 Spring Security 和 JWT&#xff0c;可以创建一个安全、可扩展的身份验证系统。下面是一个示例&#xff0c;展示如何在…

CS32 C++ programming

Here’s another random fact that I’m going to share about myself. I went to North Hollywood High School (NHHS) Highly Gifted Magnet (HGM) for school. You probably are wondering what is a Highly Gifted Magnet and how does one get there? You take an IQ t…

在Vue3中如何使用H.265视频流媒体播放器EasyPlayer.js?

H5无插件流媒体播放器EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;可支持H.264与H.265编码格式&#xff0c;性能稳定、播放流畅&#xff0c;能支持WebSocket-FLV、HTTP-FLV&#xff0c;HLS&#xff08;m3u8&#…

JAVA学习笔记29(集合)

1.集合 ​ *集合分为&#xff1a;单列集合、双列集合 ​ *Collection 接口有两个重要子接口 List Set&#xff0c;实现子类为单列集合 ​ *Map接口实现子类为双列集合&#xff0c;存放的King–Value ​ *集合体系图 1.1 Collection接口 1.接口实现类特点 1.collection实现…

CSS3 新增的新特性

一、是什么 css&#xff0c;即层叠样式表&#xff08;Cascading Style Sheets&#xff09;的简称&#xff0c;是一种标记语言&#xff0c;由浏览器解释执行用来使页面变得更美观 css3是css的最新标准&#xff0c;是向后兼容的&#xff0c;CSS1/2 的特性在 CSS3 里都是可以使用…

产品推荐 | 基于XILINX VERSAL的XW-NVME-X16-2SAS智能FPGA加速卡

01 产品概述 XW-NVME-X16-2SAS智能存储加速卡基于XILINX VERSAL ACAP MPSOC&#xff0c;EP侧提供1路PCIe GEN4 x16接口&#xff0c;RP侧最大可支持2路PCIe GEN4 x8&#xff0c;或4路PCIe GEN4 x 4&#xff0c;板载DDR4-3200MHz缓存&#xff0c;具有低延迟、高性能、低能耗、低…

1688官方API商品数据采集接口|阿里巴巴中国站获得1688商品详情 API 返回值说明

随着全球经济一体化和电子商务的快速发展&#xff0c;网络购物的需求日益增加。不断涌现的电商企业使得行业的竞争情况愈演愈烈。在这种情况下&#xff0c;企业不仅要加大经营力度&#xff0c;还要在自己的基础设施和技术上持续投入&#xff0c;才能更好的适应市场和消费习惯。…

qt windeployqt

有时候动态库 Qt5Xmld. dll 是1.79MB的合适 有时候是324kb的合适 不会报错 应该是qt编译版本的问题