Scala--静态,接口,特质,嵌套

静态属性和静态方法

思考:有一群小孩子在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩? 请使用面向对象的思想编写程序解决问题。

注意,这里是使用面向对象的思想解决问题。我第一眼看题目感觉很容易,觉得定义一个方法,来一个孩子,数量 +1 。
但是这并不是面向对象的思想


Java 中的静态概念

public static 返回值类型 方法名(参数列表){
	方法体
}

说明:Java 中静态方法并不是通过对象调用的,而是通过类对象调用的,所以静态操作并不是面向对象的。


Scala 中静态的概念–伴生对象
Scala 语言是完全面对对象(万物皆对象)的语言。所以并没有静态的操作(即在 Scala 中没有静态的概念)。
但是为了能够和Java 语言交互(Java 中有静态概念),就产生了一种特殊的对象来模拟类对象,我们称之为类的伴生对象。
这个类的所有静态内容都可以放置在它的伴生对象中声明和调用

1.当在一个文件中,有 class ScalaPersonobject ScalaPerson
2.class ScalaPerson 称为伴生类,将非静态内容写到该类中
3.object ScalaPerson 称为伴生对象,将静态内容写到该类中
4.class ScalaPerson 编译后底层生成 ScalaPerson类: ScalaPerson.class
5.object ScalaPerson 编译后底层生成 ScalaPerson$类: ScalaPerson$.class
6.对于伴生对象的内容,我们可以直接通过 ScalaPerson.属性 或者方法

在这里插入图片描述
在这里插入图片描述
小结:
1)Scala 中伴生对象采用 object 关键字声明,伴生对象中声明的全是“静态”内容,可以通过伴生对象名称直接调用
2)伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致
3)伴生对象中的属性和方法都可以荣光伴生对象名(类名)直接调用访问
4)从语法角度来讲,所谓的伴生对象其实就是类的静态方法和成员的集合
5)从技术角度来讲,Scala 还是没有生成静态的内容,只不过将伴生对象生成了一个新的类,实现属性和方法的调用
6)从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的
7)伴生对象的声明应该和伴生类的声明在同一个源码文件中(不在同一个就会出错)。但是如果没有伴生类,也就没有所谓的伴生对象,放在哪里都无所谓
8)如果 class A 独立存在,那么 A 就是一个“静态”性质的对象【即类对象】,在 object A 中声明的属性和方法可以通过A.属性 和 A.方法来实现调用
9)如果文件中同时存在伴生类和伴生对象,文件图标会变成:

解决小孩堆雪人问题:
在这里插入图片描述

伴生对象中 apply() 方法
在伴生对象中定义 apply 方法,可以实现: 类型(参数) 方式创建对象实例
在这里插入图片描述
下面这样定义是不可以的
错误提示是:Create ‘apply’ method in object Pig
在这里插入图片描述

特质

Java 接口 回顾:
声明接口:interface 接口名
实现接口: class 类名 implements 接口名1,接口名2…
富接口:该特质中既有抽象方法,又有非抽方法

1)在Java 中,一个类可以实现多个接口
2)在 Java 中,接口之间支持多继承
3)接口中属性都是常量
4)接口中的方法都是抽象的
5)接口可以对Java 的单继承进行弥补
6)接口能够解耦 //???

Scala 接口介绍:
从面向对象看,接口并不属于面向对象的语言,Scala 是纯面向对象的语言,在Scala 中,没有接口

在Scala 中,采用特质 trait (特征)来代替接口的概念
即:多个类具有相同的特征(特质)时,就可以将这个特质(特征)独立出来,采用关键字 trait 声明
理解: trait <=等价于=> interface + abstrct class

Scala 中不叫实现特质,叫继承特质
在这里插入图片描述
trait 的声明:

trait  特质名{//特质名首字母一般大写
	trait 体
}

在Scala 中,Java 的接口都可以当作特质使用

trait 的使用
1)类没有父类

class 类名  extends  特质1  with  特质2  with  特质3 ...

2)类有父类

class 类名  extends  父类  with   特质1  with  特质2  with  特质3 ...

trait 的案例
可以把特质看作是对继承的一种补充
在这里插入图片描述
上面代码的示意图
在这里插入图片描述
特质的说明
1)特质可以同时拥有抽象方法和具体方法

当一个trait 有抽象方法和非抽象方法时
trait 在底层对应
Trait.class 接口
还对应 Trait$class.class 这个抽象类

当trait 有接口和抽象类时
class Jack extend Trait 在底层对应
class Jack implements Trait —实现接口
挡在 Jack 类中要使用 Trait 的实现的方法,就通过 Trait$class 调用 —继承抽象类
在这里插入图片描述

2)特质中没有实现的方法就是抽象方法,类通过extends 继承特质,通过 with 继承多个特质

3)所有的 Java 接口都可以当作Scala 特质使用

动态混入

动态混入真正实现了解耦

1)除了可以在类声明时继承特质外,还可以在构建对象时混入特质,扩展目标类的功能

2)此种方式也可以应用于抽象类功能进行扩展

3)动态混入时Scala 特有的方式,Java 中没有动态混入,可在不修改类声明/定义的情况下,扩展类的功能,非常灵活,耦合性低【ocp闭合原则,修改源码关闭,功能扩展开放】

4)动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能

5)同时要注意动态混入时,如果抽象类有抽象方法,如何混入
在这里插入图片描述

Scala 种创建对象共有几种方式?

**
1) new 对象
2) apply 创建
3) 匿名子类方式
4) 动态混入**

叠加特质

构建对象的同时如果混入多个特质,称之为叠加特质
那么特质声明顺序从左到右,方法执行顺序从右到左
注意其中的super 方法
当执行一个动态混入对象的方法时,其执行顺序是从右往左开始执行
当执行到super 时,指的时左边的特质
如果左边没有特质,super 就是父特质
*/
案例

package com.amhu.yx.test1.xh
object TraitTest {
  def main(args: Array[String]): Unit = {
    /*说明:
    1.创建MySQL的实例时,动态的混入 DB 和 File
      创建动态混入对象时,其顺序时怎么样的
      从从往右开始执行
    2.当执行一个动态混入对象的方法时,其执行顺序是怎么样的
      从右往左开始执行
      当执行到super 时,指的时左边的特质
      如果左边没有特质,super 就是父特质
    */
    val mysql = new MySQL with DB with File
    println(mysql)

    mysql.insert(100)
  }
}
trait Operate{
  println("Operate")
  def insert(id: Int)
}
trait Data extends  Operate{
  println("Data")
  //继承的是父特质的抽象方法,可以不加 override
  override def insert(id: Int): Unit = {//实现/重写 insert
    println("插入数据 = " + id)
  }
}
trait DB extends Data{
  println("DB")
  //继承的是父类的普通方法,重写必须加入 override
  override def insert(id: Int)={
    println("向数据库")
    super.insert(id)
  }
}

trait File extends Data{
  println("File")
  override def insert(id: Int): Unit = {
    println("向文件")
    //调用了insert 方法,这里super 在动态混入时,不一定时父类
    super.insert(id)
  }
}
class MySQL{} //普通类

输出结果:
在这里插入图片描述
叠加特质小结:
1)特质声明顺序从左到右,如果有若干个叠加的特质继承自同一父特质,先执行最左边的,再执行右边的,如果左边已经实现了父特质的方法,右边就不会再执行
2)Scala 在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行(即先执行有右边特质中的方法,再执行左边特质中的方法)
3)Scala 中特质中如果调用 super ,并不是调用父特质的方法,而是向前面(左边)继续查找特质,若找不到,才回去父特质查找
4)如果想要调用具体特质的方法,可以指定

super[特质].xxx(...)

其中的泛型必须是该特质的直接超类类型
在这里插入图片描述

特质中重写抽象方法特例

再特质中重写抽象方法
理解 abstract override 的小技巧:
可以理解成,当我们给某个方法增加了abstract override 后,就是明确的告诉编译器,该方法确实是重写了父特质的抽象方法,但是重写后,该方法仍然是一个抽象方法(因为没有完全实现,需要其它特质继续实现【通过混入顺序】)
在这里插入图片描述

特质中的字段

特质中初始化了的字段就是具体字段,不初始化的字段就是抽象字段
混入该特质的类就具有了该字段。
字段不是继承,而是直接加入类,称为自己的字段

特质中未被初始化的字段(抽象字段)在具体的子类中必须被重写

特质构造顺序

介绍:特质也是有构造器的,构造器中的内容由 “字段的初始化”和一些其它的语句构成
见前面的 特质叠加

第一种特质构造顺序(声明类的同时混入特质)

1.调用当前类的超类构造器
2.第一个特质的父特质构造器
3.第一个特质构造器
4.第二个特质构造器的父特质构造器,如果已经执行过,就不再执行
5.第二个特质构造器
6…重复4,5 的步骤(如果有第3个,第4 个特质)
7.当前类构造器

第二种特质构造顺序(构建对象时,动态混入特质)

1.调用当前类的超类构造器
2.当前类构造器
3.第一个特质构造器的父特质构造器
4.第一个特质构造器
5.第二个特质构造器的父特质构造器,如果已经执行过,就不再执行
6.第二个特质构造器
7…重复5,6的步骤(如果有第3,4个特质)

两种方式对构造顺序的影响
第一种方式实际是构建类对象,在混入特质时,该对象还没有创建
第二种方式实际是构造匿名子类,可以理解成在混入特质时,对象已经创建了

扩展类的特质

特质可以继承类,以用来扩展该类的一些功能

所有混入该特质的类,会自动成为那个特质所继承的超类的子类
在这里插入图片描述

如果混入该特质的类,已经继承了另一个类(A类)
则要求 A类 是特质超类的子类,否则就会出现多继承现象,发生错误
在这里插入图片描述

特质的自身类型

为了解决特制的循环依赖问题
同时确保特质在不扩展某个类的情况下,依然可以做到 限制混入该特质的类的类型 self-type
不太懂
在这里插入图片描述

嵌套类

Java 中,类有五大成员,请问是哪五大成员
1.属性
2.方法
3.内部类
4.构造器
5.代码块

Java 内部类简单回顾
Java 中,一个类的内部又完整的嵌套了另一个完整的类结构,
被嵌套的类称为内部类,嵌套其他类的类称为外部类,
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
Java 中将内部类当作一个属性看待

Java 内部类的分类
从定义再外部类的成员位置上来看
1)成员内部类(没用 static 修饰)
2)静态内部类(使用 static 修饰)

定义在外部类局部位置上(比如方法内)来看:
1)分为局部内部类(有类名)
2)匿名内部类(没有类名)


在Scala 中,几乎可以在任何语法结构中内嵌任何语法结构,如在类中可以再定义一个类,这样的类是嵌套类,其它语法结构也是一样
嵌套类 类似于Java 中的内部类

在内部中访问外部类的属性:

1》可以通过外部类对象访问
访问方式:

外部类名.this.属性名

在这里插入图片描述
在这里插入图片描述

2》可以通过外部类别名访问(推荐
访问方式:

外部类名别名.属性名

在这里插入图片描述

类型投影

类型投影是指:
在方法声明上,如果使用 外部类#内部类 的方法,表示忽略内部类的对象关系
等同于Java 中内部类的语法操作,我们将这种方式称之为类型投影
即:忽略对象的创建方式,只考虑类型

默认情况下,Scala 中的内部类的实例的和创建该内部类的外部对象相关联
在这里插入图片描述

加入类型投影:

在这里插入图片描述

—韩顺平老师的Scala笔记

附:
十二生肖:🐀🐂🐅🐇🐉🐍🐎🐏🐒🐓🐕🐖

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值