七、Scala面向对象编程(高级)

淩亂°似流年 2024-04-17 05:59 162阅读 0赞

=

目录

一、静态属性和静态方法

1、基本介绍

2、伴生对象

(1)实例

(2)伴生对象的小结

(3)伴生对象-apply方法

三、接口

1、Java接口

2、Scala接口(特质)

A、基本介绍

B、特质入门实例

C、带有特质的对象,动态混入

E、叠加特质

F、在特质中重写抽象方法特例

G、富接口特质、特质中的具体字段和抽象字段

H、特质构造顺序

I、扩展类的特质

J、自身类型

四、嵌套类

A、Java内部类的简单回顾

(2)Java内部类的分类

B、Scala嵌套类的使用

C、类型投影


一、静态属性和静态方法

1、基本介绍

(1)回顾Java的静态概念

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

  1. public static 返回值类型 方法名(参数列表) {方法体}
  2. 静态属性...

(2)Scala中静态的概念-伴生对象

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

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3MyOTQ4NzgzMDQ_size_16_color_FFFFFF_t_70

2、伴生对象

(1)实例

  1. //伴生类
  2. //class ScalaPerson 编译后生成ScalaPerson 类 ScalaPerson.class
  3. //object ScalaPerson 编译后生成ScalaPerson$ 类 ScalaPerson$.class
  4. class ScalaPerson{
  5. var name:String = _
  6. }
  7. //伴生对象
  8. object ScalaPerson {
  9. var sex:Boolean = true
  10. def sayHi():Unit={
  11. println("object ScalaPerson")
  12. }
  13. }

(2)伴生对象的小结

  • Scala中伴生对象采用object关键字声明,伴生对象中声明的全是 “静态”内容,可以通过伴生对象名称直接调用
  • 伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
  • 伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问
  • 从语法角度来讲,所谓的伴生对象其实就是类的静态方法和成员的集合
  • 从技术角度来讲,scala还是没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用。
  • 从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的。
  • 伴生对象的声明应该和伴生类的声明在同一个源码文件中(如果不在同一个文件中会运行错误!),但是如果没有伴生类,也就没有所谓的伴生对象了,所以放在哪里就无所谓了。
  • 如果 class A 独立存在,那么A就是一个类, 如果 object A 独立存在,那么A就是一个”静态”性质的对象[即类对象], 在 object A中声明的属性和方法可以通过 A.属性 和 A.方法 来实现调用
  • 当一个文件中,存在伴生类和伴生对象时,文件的图标会发生变化

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3MyOTQ4NzgzMDQ_size_16_color_FFFFFF_t_70 1

(3)伴生对象-apply方法

  1. object ApplyTest {
  2. def main(args: Array[String]): Unit = {
  3. val list = List(1,2,3)
  4. val pig = new Pig("小花")
  5. // 使用apply方法来创建对象
  6. val pig2 = Pig("小黑猪")//触发def apply(pName: String): Pig = new Pig(pName)方法
  7. val pig3 = Pig()//触发def apply(): Pig = new Pig("匿名猪猪")
  8. println(s"pig2 = ${pig2.name}")
  9. println(s"pig3 = ${pig3.name}")
  10. }
  11. }
  12. //apply方法演示
  13. class Pig(pName:String){
  14. var name:String = pName
  15. }
  16. object Pig{
  17. // 编写apply方法
  18. def apply(pName: String): Pig = new Pig(pName)
  19. def apply(): Pig = new Pig("匿名猪猪")
  20. }

二、单例对象

(在scala设计模式中学习)

三、接口

1、Java接口

(1)声明接口

  1. interface 接口名

(2)实现接口

  1. class 类名 implements 接口名1,接口2

(3)注意点

  • 在Java中, 一个类可以实现多个接口。
  • 在Java中,接口之间支持多继承
  • 接口中属性都是常量
  • 接口中的方法都是抽象的

2、Scala接口(特质)

A、基本介绍

从面向对象来看,接口并不属于面向对象的范畴,Scala是纯面向对象的语言,在Scala中,没有接口。

Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特征(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。 理解trait 等价于(interface + abstract class)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3MyOTQ4NzgzMDQ_size_16_color_FFFFFF_t_70 2

(1)trait 的声明

trait 命名 一般首字母大写.

在scala中,java中的接口可以当做特质使用

  1. trait 特质名 {
  2. trait
  3. }
  4. //trait Serializable extends Any with java.io.Serializable
  5. //scala中,java的接口都可以当做trait来使用
  6. object T1 extends Serializable{
  7. }

(2)trait 的使用

一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接。

  1. //假如定义的类没有父类时
  2. class 类名 extends 特质1 with 特质2 with 特质3 ..
  3. //假如定义的类有父类时
  4. class 类名 extends 父类 with 特质1 with 特质2 with 特质3

B、特质入门实例

  • 可以把特质可以看作是对继承的一种补充
  • Scala的继承是单继承,也就是一个类最多只能有一个父类,这种单继承的机制可保证类的纯洁性,比c++中的多继承机制简洁。但对子类功能的扩展有一定影响.所以我们认为: Scala引入trait特征 第一可以替代Java的接口, 第二个也是对单继承机制的一种补充
  • Scala提供了特质(trait) ,特质可以同时拥有抽 象方法和具体方法,一 个类可以实现/继承多个 特质,具体方法可以不被实现特质的类重写,也可以被实现特质的类重写。和Java中的接口不太一样的是特质中的方法并不一定是抽象的,
  • 特质中没有实现的方法就是抽象方法。类通过extends继承特质,通过with可以继承多个特质
  • 所有的java接口都可以当做Scala特质使用

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3MyOTQ4NzgzMDQ_size_16_color_FFFFFF_t_70 3

  1. object TraitTest {
  2. def main(args: Array[String]): Unit = {
  3. val c = new C
  4. val e = new E
  5. c.getConnect()
  6. e.getConnect()
  7. c.sayOk()
  8. e.sayOk()
  9. }
  10. }
  11. /**类之间的关系,使用trait给C和E增加方法(如数据库连接方法)*/
  12. class A{}
  13. class B extends A{}
  14. class C extends A with T {
  15. override def getConnect(): Unit = {
  16. println("连接mysql...")
  17. }
  18. }
  19. class D{}
  20. class E extends D with T {
  21. override def getConnect(): Unit ={
  22. println("连接mongo...")
  23. }
  24. //重写具体方法
  25. override def sayOk(): Unit = {
  26. println("mongo say ok")
  27. }
  28. }
  29. class F extends D{}
  30. /** 定义trait*/
  31. trait T{
  32. // 定义一个规范
  33. def getConnect()
  34. // 具体方法
  35. def sayOk():Unit={
  36. println("okkkk")
  37. }
  38. }

C、带有特质的对象,动态混入

(1)基本介绍

  • 除了可以在类声明时继承特质以外,还可以在构建对象时混入特质,扩展目标类的功能
  • 此种方式也可以应用于对抽象类功能进行扩展
  • 动态混入是Scala特有的方式(java没有动态混入),可在不修改类声明/定义的情况下,扩展类的功能,非常的灵活,耦合性低。
  • 动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能。

    object TraitTest {
    def main(args: Array[String]): Unit = {
    // 普通类的动态混入

    1. var oracle = new OracleDB with T
    2. oracle.insert(111)
    3. oracle.update(222)

    // 抽象类的动态混入

    1. val mysql = new MysqlDB with T
    2. mysql.insert(333)

    }
    }
    class OracleDB extends T2 {
    }
    abstract class MysqlDB {
    }
    trait T {
    def insert(id: Int): Unit = {

    1. println("插入数据 = " + id)

    }
    }
    trait T2{
    def update(id:Int): Unit ={

    1. println(s"更新数据 = $id")

    }
    }

(2)如果抽象类中有抽象的方法,如何动态混入特质?

  1. object TraitTest {
  2. def main(args: Array[String]): Unit = {
  3. // 具有抽象方法的抽象类,如何动态混入特质
  4. val mysql = new MysqlDB with T {
  5. override def sayOk(): Unit = {
  6. println("具有抽象方法的抽象类,如何动态混入特质")
  7. }
  8. }
  9. mysql.insert(111)
  10. mysql.sayOk()
  11. }
  12. }
  13. abstract class MysqlDB{
  14. def sayOk()
  15. }
  16. trait T {
  17. def insert(id: Int): Unit = {
  18. println("插入数据 = " + id)
  19. }
  20. }

E、叠加特质

构建对象的同时如果混入多个特质,称之为叠加特质那么特质声明顺序从左到右,方法执行顺序从右到左。

(1)叠加特质时,对象的构建顺序,和执行方法的顺序分析

  1. object TraitTest {
  2. def main(args: Array[String]): Unit = {
  3. // 混入多个特质的特点
  4. // 1、当动态混入多个特质时,顺序是怎么样子的
  5. // Scala在叠加特质的时候,会首先从后面的特质开始执行(从右向左)
  6. /** T_1 ...
  7. * data_T ...
  8. * DB_T ...
  9. * File_T ...
  10. */
  11. val mysql = new Mysql with DB_T with File_T
  12. println("======================")
  13. /*** File_T 插入数据 = 111
  14. * DB_T 插入数据 = 111
  15. */
  16. // 2、当执行一个动态混入对象的方法,其执行顺序是,从右向左开始执行(栈)
  17. // 所以先输出 File_T 插入数据 = 111
  18. // 当执行到 super 时,是指的左边的特质 DB_T,若没有调用super方法,则不会像后执行(即执行完毕)
  19. // 若执行super时,左边没有特质了,super指的当前特质的父类
  20. mysql.insert(111)
  21. }
  22. }
  23. /**类*/
  24. class Mysql{}
  25. /**多个特质*/
  26. trait T_1 {
  27. println("T_1 ...")
  28. def insert(id: Int): Unit = {
  29. println("T_1 插入数据 = " + id)
  30. }
  31. }
  32. trait Data_T extends T_1{
  33. println("data_T ...")
  34. override def insert(id: Int): Unit = {
  35. println("data_T 插入数据 = " + id)
  36. }
  37. }
  38. trait DB_T extends Data_T{
  39. println("DB_T ...")
  40. override def insert(id: Int): Unit = {
  41. println("DB_T 插入数据 = " + id)
  42. super.insert(id)
  43. }
  44. }
  45. trait File_T extends Data_T{
  46. println("File_T ...")
  47. override def insert(id: Int): Unit = {
  48. println("File_T 插入数据 = " + id)
  49. //调用了insert方法,这里的super和混入特质的顺序有关系。不一定指的是父特质data_T
  50. super.insert(id)
  51. }
  52. }

(2)叠加特质注意事项和细节

  • 特质声明顺序从左到右。
  • Scala在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行
  • Scala中特质中如果调用super,并不是表示调用父特质的方法,而是向前面(左边)继续查找特质,如果找不到,才会去父特质查找
  • 如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛型必须是该特质的直接超类类型

F、在特质中重写抽象方法特例

(1)下述代码运行时会报错

  1. trait T_1 {
  2. def insert(id: Int)
  3. }
  4. trait Data_T extends T_1{
  5. def insert(id: Int): Unit = {
  6. println("data_T 插入数据 = " + id)
  7. super.insert(id)
  8. }
  9. }

(2)原因:没有完全的实现insert,同时还没有声明 abstract overrid

(3)解决方法

  • a、去掉 super()
  • b、调用父特质的抽象方法,那么在实际使用时,没有方法的具体实现,无法编译通过,为了避免这种情况的发生。可重写抽象方法,这样在使用时,就必须考虑动态混入的顺序问题。

    object TraitTest {
    def main(args: Array[String]): Unit = {
    // 尝试去掉Data_T中方法insert中调用的super.insert(id),看会发现什么

    1. val mysql = new Mysql with DB_T with Data_T
    2. mysql.insert(666)
    3. //下面代码会报错,和动态混入顺序有关,执行到Data_T时中的方法 super.insert(id)会取找到父类的抽象方法
    4. val mysql2 = new Mysql with Data_T with DB_T

    }
    }
    /类*/
    class Mysql{}
    /
    特质*/
    trait T_1 {
    def insert(id: Int)
    }
    trait Data_T extends T_1{
    //若
    // 1、在子特质中重写父特质的方法
    // 2、同时又调用了super方法,
    // 则必须使用 abstract override 修饰
    // 作用:这时super.insert(id)的调用和动态混入的顺序有关系(参考上面内容)
    abstract override def insert(id: Int): Unit = {

    1. println("data_T 插入数据 = " + id)
    2. super.insert(id)

    }
    }
    //实现T_1中的抽象方法
    trait DB_T extends T_1{
    def insert(id: Int): Unit = {

    1. println("DB_T 插入数据 = " + id)

    }
    }

(4)如何理解abstract override

可以这里理解,当我们给某个方法增加了abstract override 后,就是明确的告诉编译器,该方法确实是重写了父特质的抽象方法,但是重写后,该方法仍然是一个抽象方法(因为没有完全的实现,需要其它特质继续实现[通过混入顺序]

G、富接口特质、特质中的具体字段和抽象字段

富接口:即该特质中既有抽象方法,又有非抽象方法

具体字段:特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。混入该特质的类就具有了该字段字段不是继承,而是直接加入类,成为自己的字段,可以通过编译后的代码查看。

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

  1. object TraitTest {
  2. def main(args: Array[String]): Unit = {
  3. val mysql = new Mysql with T {
  4. override var age = 0
  5. }
  6. println(mysql.age)
  7. }
  8. }
  9. class Mysql{}
  10. /**特质*/
  11. trait T {
  12. var name :String = ""
  13. var age :Int
  14. def insert():Unit={
  15. }
  16. }

H、特质构造顺序

特质也是有构造器的,构造器中的内容由“字段的初始化” 和一些其他语句构成。具体实现请参考“特质叠加”

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

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

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

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

    object TraitTest {
    def main(args: Array[String]): Unit = {
    // 声明类时混入特质

    1. val ff1 = new FF()//构造顺序 E...A...B....C....D....F....
    2. println(ff1)
    3. println("=======================")

    // 动态混入特质

    1. val ff2 = new KK() with CC with DD//构造顺序 E...K....A...B....C....D....
    2. println(ff2)

    }
    }
    trait AA {
    println(“A…”)
    }
    trait BB extends AA {
    println(“B….”)
    }
    trait CC extends BB {
    println(“C….”)
    }
    trait DD extends BB {
    println(“D….”)
    }
    class EE {
    println(“E…”)
    }
    class FF extends EE with CC with DD {
    println(“F….”)
    }
    class KK extends EE {
    println(“K….”)
    }

(3)分析两种方式对构造顺序的影响

  • 第1种方式实际是构建类对象, 在混入特质时,该对象还没有创建。
  • 第2种方式实际是构造匿名子类,可以理解成在混入特质时,对象已经创建了。

I、扩展类的特质

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

所有混入该特质的类,会自动成为那个特质所继承的超类的子类

  1. trait LoggedException extends Exception{
  2. def log(): Unit ={
  3. println(getMessage) // 方法来自于Exception类
  4. }
  5. }
  6. //UnhappyException 就是Exception的子类.
  7. class UnhappyException extends LoggedException{
  8. // 已经是Exception的子类了,所以可以重写方法
  9. override def getMessage = "错误消息!"
  10. }

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

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3MyOTQ4NzgzMDQ_size_16_color_FFFFFF_t_70 4

J、自身类型

自身类型:主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型。

  1. //class Console extends Logger {} //error,因为不是Exception或其子类
  2. class Console extends Exception with Logger//正确,
  3. //Logger就是自身类型特质,当这里做了自身类型后
  4. //等价于trait Logger extends Exception,主要是为了给编译器说明,要求混入该特质的对象也必须是Exception的子类
  5. trait Logger {
  6. // 明确告诉编译器,我就是Exception,如果没有这句话,下面的getMessage不能调用
  7. this: Exception =>
  8. def log(): Unit ={
  9. // 既然我就是Exception, 那么就可以调用其中的方法
  10. println(getMessage)
  11. }
  12. }

四、嵌套类

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

A、Java内部类的简单回顾

在Java中,一个类的内部又完整的嵌套了另一个完整的类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类。内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

(1)基本语法

  1. class Outer{
  2. 外部类
  3. class Inner{
  4. 内部类
  5. }
  6. }
  7. class Other{
  8. 外部其他类
  9. }

(2)Java内部类的分类

从定义在外部类的成员位置上来看,

  • 1) 成员内部类(没用static修饰)
  • 2) 和静态内部类(使用static修饰),

定义在外部类局部位置上(比如方法内)来看:

  • 分为局部内部类(有类名)
  • 匿名内部类(没有类名)

B、Scala嵌套类的使用

(1)定义Scala 的成员内部类和静态内部类,并创建相应的对象实例。

  1. object TraitTest {
  2. def main(args: Array[String]): Unit = {
  3. val outer1 : ScalaOuterClass = new ScalaOuterClass()
  4. val outer2 : ScalaOuterClass = new ScalaOuterClass()
  5. // Scala创建内部类的方式和Java不一样,将new关键字放置在前,使用 对象.内部类 的方式创建
  6. // 对象.内部类 的方式创建,这里的语法可以看出在scala中,默认情况下,内部类实例和外部对象关联
  7. val inner1 = new outer1.ScalaInnerClass()
  8. val inner2 = new outer2.ScalaInnerClass()
  9. //创建静态内部类对象
  10. val staticInner = new ScalaOuterClass.ScalaStaticInnerClass()
  11. println(staticInner)
  12. }
  13. }
  14. class ScalaOuterClass {
  15. class ScalaInnerClass { //成员内部类
  16. }
  17. }
  18. object ScalaOuterClass { //伴生对象
  19. class ScalaStaticInnerClass { //静态内部类
  20. }
  21. }

(2)在内部类中访问外部类的属性。

a、内部类如果想要访问外部类的属性,可以通过外部类对象访问。 即:访问方式:外部类名.this.属性名

  1. object TraitTest {
  2. def main(args: Array[String]): Unit = {
  3. //调用成员内部类的方法
  4. val outer = new ScalaOuterClass
  5. val inner = new outer.ScalaInnerClass
  6. inner.info()
  7. }
  8. }
  9. class ScalaOuterClass {
  10. var name : String = "scott"
  11. private var sal : Double = 1.2
  12. class ScalaInnerClass { //成员内部类
  13. def info() = {
  14. // 访问方式:外部类名.this.属性名
  15. // 怎么理解 ScalaOuterClass.this 就相当于是 ScalaOuterClass 这个外部类的一个实例,
  16. // 然后通过 ScalaOuterClass.this 实例对象去访问 name 属性
  17. // 只是这种写法比较特别,学习java的同学可能更容易理解 ScalaOuterClass.class 的写法.
  18. println("name = " + ScalaOuterClass.this.name + " age =" + ScalaOuterClass.this.sal)
  19. }
  20. }
  21. }
  22. object ScalaOuterClass { //伴生对象
  23. class ScalaStaticInnerClass { //静态内部类
  24. }
  25. }

b、内部类如果想要访问外部类的属性,也可以通过外部类别名访问(推荐)。 即:访问方式:外部类名别名.属性名

  1. object TraitTest {
  2. def main(args: Array[String]): Unit = {
  3. //调用成员内部类的方法
  4. val outer = new ScalaOuterClass
  5. val inner = new outer.ScalaInnerClass
  6. inner.info()
  7. }
  8. }
  9. class ScalaOuterClass {
  10. myOuter => //这样写,你可以理解成这样写,myOuter就是代表外部类的一个对象.
  11. // 当给外部指定别名时,需要将外部类的属性放到别名后.
  12. var name : String = "scott"
  13. private var sal : Double = 1.2
  14. class ScalaInnerClass { //成员内部类
  15. def info() = {
  16. println("name = " + ScalaOuterClass.this.name + ", age =" + ScalaOuterClass.this.sal)
  17. println("-----------------------------------")
  18. println("name = " + myOuter.name + ", age =" + myOuter.sal)
  19. }
  20. }
  21. }
  22. object ScalaOuterClass { //伴生对象
  23. class ScalaStaticInnerClass { //静态内部类
  24. }
  25. }

C、类型投影

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

  1. object ShadowTest {
  2. def main(args: Array[String]): Unit = {
  3. val outer1 : ScalaOuterClass = new ScalaOuterClass()
  4. val outer2 : ScalaOuterClass = new ScalaOuterClass()
  5. val inner1 = new outer1.ScalaInnerClass()
  6. val inner2 = new outer2.ScalaInnerClass()
  7. inner1.test(inner1) // ok, 因为 需要outer1.ScalanInner
  8. // 默认情况下,scala的内部类实例是和创建该内部类实例的外部类对象关联的(即一一对应的)
  9. inner1.test(inner2) // error, 需要outer1.ScalanInnerouter2.ScalanInner,使用类型投影后就不会报错
  10. }
  11. }
  12. class ScalaOuterClass {
  13. myOuter =>
  14. class ScalaInnerClass { //成员内部类
  15. // 下面这个方法可以接收又外部类派生出来的任何内部类 ScalaInnerClass 实例
  16. // 下面的 ScalaOuterClass#ScalaInnerClass 类型投影的作用就是屏蔽外部对象对内部类对象的影响
  17. def test(ic: ScalaOuterClass#ScalaInnerClass): Unit = {
  18. System.out.println("使用类型投影 :"+ic)
  19. }
  20. }
  21. }

发表评论

表情:
评论列表 (有 0 条评论,162人围观)

还没有评论,来说两句吧...

相关阅读