8.1Scala中静态的概念-伴生对象
Scala语言是完全面向对象(万物皆对象)的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,我们称之为类的伴生对象。这个类的所有静态内容都可以放置在它的伴生对象中声明和调用
8.1.1伴生对象的快速入门
object ClassObjectDemo {
def main(args: Array[String]): Unit = {
//使用伴生对象的方法/属性
//伴生对象.属性 或 方法
ScalaPerson.sayHi()
println(ScalaPerson.sex)
}
}
//伴生类说明
//1. 伴生类和伴生对象一一对应
//2. class ScalaPerson 底层会生成一个类 ScalaPerson
//3. 在底层生成类 ScalaPerson 会去调用 ScalaPerson$ 的方法。。。
class ScalaPerson {
var name : String = _
}
//伴生对象
//1. 伴生对象和伴生类也是一一对应
//2. object ScalaPerson , 当在一个文件中,只有object ScalaPerson 会生成两个类ScalaPerson(空) 和 ScalaPerson$
//3. 当在一个文件中,有object ScalaPerson 和 class ScalaPerson,那么就生成一个 ScalaPerson$
object ScalaPerson {
var sex : Boolean = true //
def sayHi(): Unit = {
println("object ScalaPerson sayHi")
}
}
8.1.2伴生对象的小结
1)Scala中伴生对象采用object关键字声明,伴生对象中声明的全是 "静态"内容,可以通过伴生对象名称直接调用。
2)伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
3)伴生对象中的属性和方法都可以通过伴生对象名直接调用访问
4)从语法角度来讲,所谓的伴生对象其实就是类的静态方法和静态变量的集合
5)从技术角度来讲,scala还是没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用。[反编译看源码]
6)从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的。
7)伴生对象的声明应该和伴生类的声明在同一个源码文件中(如果不在同一个文件中会运行错误!),但是如果没有伴生类,也就没有所谓的伴生对象了,所以放在哪里就无所谓了。
8)如果 class A 独立存在,那么A就是一个类, 如果 object A 独立存在,那么A就是一个"静态"性质的对象[即类对象], 在 object A中声明的属性和方法可以通过 A.属性 和 A.方法 来实现调用
8.1.3伴生对象解决小孩游戏问题
如果,设计一个var total Int表示总人数,我们在创建一个小孩时,就把total加1,并且 total是所有对象共享的就ok了!,我们使用伴生对象来解决
object ChildGame {
def main(args: Array[String]): Unit = {
val child1 = new Child("蜘蛛精")
val child2 = new Child("老鼠精")
val child3 = new Child("黄鼠狼精")
Child.joinGame(child1) //Child$.MODULE$
Child.joinGame(child2)
Child.joinGame(child3)
Child.showInfo()
}
}
//小孩类
class Child(cName:String) {
var name :String = cName
}
object Child {
var totalNum = 0
def joinGame(child:Child): Unit = {
println(child.name + " 加入游戏..")
totalNum += 1
}
def showInfo(): Unit = {
printf("当前有%d个小孩玩游戏\n" , totalNum)
}
}
8.1.4伴生对象-apply方法
object ApplyDemo {
def main(args: Array[String]): Unit = {
//传统的创建... new ....
val cat1 = Cat("波斯猫",2)
println(cat1)
val cat2 = Cat(10)
println(cat2)
}
}
class Cat(cName:String,cAge:Int) {
var name:String = cName
var age:Int = cAge
override def toString: String = {
s"猫猫的信息 name=$cName age=$age"
}
def this(age:Int) {
this("默认小猫", age)
//其它代码...
}
}
object Cat {
//apply可以重载..
def apply(cName: String, cAge: Int): Cat =
new Cat(cName, cAge) //调用了 class Cat的构造器
def apply(cAge: Int): Cat = new Cat(cAge)
}
8.2单例模式
8.2.1什么是单例模式
单例模式是指:保证在整个的软件系统中,某个类只能存在一个对象实例。
8.2.2单例模式的应用场景
比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就够,这是就会使用到单例模式。
Akka [ActorySystem 单例] AKKA
8.2.3单例模式的应用案例
单例模式-懒汉式
object TestSingleTon extends App {
val singleTon = SingleTon.getInstance
val singleTon2 = SingleTon.getInstance
println(singleTon.hashCode() + " " + singleTon2.hashCode())
println(singleTon == singleTon2) //true
}
//将SingleTon的构造方法私有化
//实现了SingleTon 类的单例模式
class SingleTon private() {}
object SingleTon {
private var s: SingleTon = null
//懒汉式的单例
def getInstance = {
if (s == null) {
s = new SingleTon
}
s
}
}
单例模式-饿汉式
package com.atguigu.chapter08
object TestSingleTon2 extends App {
val singleTon = SingleTon2.getInstance
val singleTon2 = SingleTon2.getInstance
println(singleTon.hashCode() + " ~ " + singleTon2.hashCode())
println(singleTon == singleTon2)
}
//将SingleTon2的构造方法私有化
class SingleTon2 private() {
println("~~~")
}
object SingleTon2 {
private val s: SingleTon2 = new SingleTon2
//饿汉式
def getInstance = {
s
}
}
8.3接口
8.3.1回顾Java接口
声明接口
interface 接口名
实现接口
class 类名 implements 接口名1,接口2
1)在Java中, 一个类可以实现多个接口。
2)在Java中,接口之间支持多继承
3)接口中属性都是常量
4)接口中的方法都是抽象的 //java高级 [默认实现 【ssm】]
8.3.2Scala接口的介绍
1)从面向对象来看,接口并不属于面向对象的范畴,Scala是纯面向对象的语言,在Scala中,没有接口。
2)Scala语言中,采用trait(特质,特征)来代替接口的概念,也就是说,多个类具有相同的特征(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。可以这里理解trait (scala的特质时 抽象类+接口 组合 分析底层)
8.4特质(trait)
trait 的声明
trait 特质名 {
trait体
}
1)trait 命名 一般首字母大写.
Cloneable , Serializable
object T1 extends Serializable {
}
Serializable: 就是scala的一个特质。
2)在scala中,java中的接口可以当做特质使用
8.4.1Scala中trait 的使用
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接
没有父类
class 类名 extends 特质1 with 特质2 with 特质3 …
有父类
class 类名 extends 父类 with 特质1 with 特质2 with 特质3
8.4.2特质的快速入门案例
可以把特质可以看作是对继承的一种补充
Scala的继承是单继承,也就是一个类最多只能有一个父类,这种单继承的机制可保证类
的纯洁性,比c++中的多继承机制简洁。但对子类功能的扩展有一定影响.所以
我们认为: Scala引入trait特征 第一可以替代Java的接口, 第二个也是对单继承机制
的一种补充 【案例演示+代码说明反编译】
代码如下:
object Trait02 {
def main(args: Array[String]): Unit = {
//使用
val c = new C
val e = new E
//调用了C类实现的Trait1的geConnenct
c.getConnect("root","12345")
e.getConnect("scott","12345")
}
}
//这是一个Trait1
trait Trait1 {
//声明方法,抽象的.
def getConnect(user: String, pwd: String): Unit
//def test(n1:Int)
}
class A {}
class B extends A {}
class C extends A with Trait1 {
override def getConnect(user: String, pwd: String): Unit = {
println("连接到mysql数据库")
}
}
class D {}
class E extends D with Trait1 {
def getConnect(user: String, pwd: String): Unit = {
println("e连接oracle")
}
}
class F extends D {}
2)特质中没有实现的方法就是抽象方法。类通过extends继承特质,通过with可以继承多个特质
trait Trait05 {
def sayHi
}
trait Trait04 {
def say()
def sayHello(): Unit = {
println("Trait04 sayHello")
}
}
class A05 extends Trait04 with Trait05 {
override def say(): Unit = {
println("A05 say()")
}
override def sayHi: Unit = {
}
}
- 所有的java接口都可以当做Scala特质使用
8.4.4带有特质的对象,动态混入
1)除了可以在类声明时继承特质以外,还可以在构建对象时混入特质,扩展目标类的功能【反编译看动态混入本质】
2)此种方式也可以应用于对抽象类功能进行扩展
3)动态混入是Scala特有的方式(java没有动态混入),可在不修改类声明/定义的情况下,扩展类的功能,非常的灵活,耦合性低 。
4)动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能。[如何理解]
5)抽象类中有 抽象的方法,如何动态混入特质->可以,在创建实例时,实现抽象方法即可
6)代码演示:
object MixInDemo {
def main(args: Array[String]): Unit = {
//创建OracleDB 实例,同时动态混入Operate3特质
//就可以使用特质的方法,理解解耦接入.
val oracleDB = new OracleDB with Operate3 {
override def insert2(): Unit = {
println("insert2")
}
}
oracleDB.insert(100)//
oracleDB.insert2()
new MySQL3 with Operate3 {
override def insert2(): Unit = {
}
}
//如果我们要去实例化一个abstract 类,也可以,但是需要时候用匿名子类来构建
//语句
val mySQL = new MySQL3 {
override def sayHi: Unit = {
}
}
}
}
//特质
trait Operate3 {
def insert(id: Int): Unit = {
println("插入数据 = " + id)
}
def insert2()
}
//普通类
class OracleDB {
}
//抽象类
abstract class MySQL3 {
def sayHi
}
在Scala中创建对象共有几种方式
1)new 对象
2)applay 方法,创建对象
3)动态混入
4)匿名子类创建对象
8.4.5叠加特质
基本介绍
构建对象的同时如果混入多个特质,称之为叠加特质,那么特质声明顺序从左到右,方法执行顺序从右到左。
叠加特质应用案例
目的:分析叠加特质时,对象的构建顺序,和执行方法的顺序
object MuTraitDemo {
def main(args: Array[String]): Unit = {
//1. Operate4...
//2. Data4
//3. DB4
//4. File4
//val mysql = new MySQL4 with DB4 with File4 //栈的机制
val mysql = new MySQL4 with File4 with DB4
//分析问题1: 动态混入时,构建对象实例的顺序是什么?
//构建实例时,顺序是从左到右 -》
//分析问题2:动态混入创建对象,在执行方法时,顺序
//是 从右到左执行
// 1. 向文件
// 2. 向数据库
// 3. 插入数据 = 100
println("-----------------------")
mysql.insert(100)
//val mysql = new MySQL4 with File4 with DB4
}
}
trait Operate4 {
println("Operate4...")
def insert(id: Int)
}
trait Data4 extends Operate4 {
println("Data4")
override def insert(id: Int): Unit = {
println("插入数据 = " + id)
}
}
trait DB4 extends Data4 {
println("DB4")
override def insert(id: Int): Unit = {
println("向数据库")
super.insert(id)
}
}
trait File4 extends Data4 {
println("File4")
override def insert(id: Int): Unit = {
println("向文件")
//理论上应该是调用其父特质的insert
//这里的 super 含义: Scala中特质中如果调用super,并不是表示调用父特质的方法,而是向前面(左边)继续查找特质,如果找不到,才会去父特质查找
super.insert(id)
//如果我们就是希望去让super指向自己的直接父特质,可以如下操作
//这里的Data4必须是File4 直接父特质
//super[Data4].insert(id)
}
}
//普通的类
class MySQL4 {}
叠加特质注意事项和细节
1)特质声明顺序从左到右。
2)Scala在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行
3)Scala中特质中如果调用super,并不是表示调用父特质的方法,而是向前面(左边)继续查找特质,如果找不到,才会去父特质查找
4)如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛型必须是该特质的直接超类类型
8.5在特质中重写抽象方法特例
8.5.1提出问题和应用案例
object OverrideAbstractMethod {
def main(args: Array[String]): Unit = {
println("aaa~~")
val mysql5 = new MySQL5 with DB5 with File5 //ok!!!
//val mysql5_ = new MySQL5 with File5 with DB5//error!!!
mysql5.insert(999) //1.
}
}
trait Operate5 {
def insert(id: Int)
}
trait DB5 extends Operate5 {
def insert( id : Int ): Unit = {
println("将数据保存到数据库中.." + id)
}
}
trait File5 extends Operate5 {
//这里的问题1. 重写了父特质的方法,但是没有没有完全完成,因为调用了父特质的抽象方法
//解决方法.
//(1)abstract override 表示告诉编译器,该方法重写,但是仍然是抽象,但是这里的super.insert()
//(2)不是执行父特质,而是在动态混入时,指向前面的某个特质的super.insert
//(3)我们在混入 File5 要注意混入的顺序。
abstract override def insert(id: Int): Unit = {
println("将数据保存到文件中..")
super.insert(id)
}
}
class MySQL5 {}
8.5.2解决问题重写抽象方法
方式1 : 去掉 super()...
方式2: 调用父特质的抽象方法,那么在实际使用时,没有方法的具体实现,无法编译通过,为了避免这种情况的发生。可重写抽象方法,这样在使用时,就必须考虑动态混入的顺序问题。
重写抽象方法时需要考虑混入特质的顺序问题和完整性问题
看4个案例,并判断结果。
var mysql2 = new MySQL5 with DB5 // ok
mysql2.insert(100)
var mysql3 = new MySQL5 with File5 // error
mysql2.insert(100)
var mysql4 = new MySQL5 with File5 with DB5// error
mysql4.insert(100)
var mysql4 = new MySQL5 with DB5 with File5// ok
mysql4.insert(100)
8.5.3当作富接口使用的特质
富接口:即该特质中既有抽象方法,又有非抽象方法
8.5.4特质中的具体字段
特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。混入该特质的类就具有了该字段,字段不是继承,而是直接加入类,成为自己的字段。
案例演示:
object MixInFiledDemo {
def main(args: Array[String]): Unit = {
var mysql = new MySQL6 with DB6
//通过反编译,可以看到 opertype,是加入到 mysql对象
//从这里我们可以看到混入和继承不是一回事
/*
MySQL6 mysql = new MySQL6() { private String opertype;
public String opertype() { return this.opertype; }
@TraitSetter
public void opertype_$eq(String x$1) { this.opertype = x$1; }
public void insert() { DB6.class.insert(this); }
};
*/
println(mysql.opertype)
}
}
//特质
trait Operate6 {
var opertype: String //抽象字段
def insert() //抽象方法
}
trait DB6 extends Operate6 {
var opertype: String = "insert" //字段
def insert(): Unit = { //实现
}
}
class MySQL6 {}
8.5.5特质构造顺序
介绍
特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成。具体实现请参考“特质叠加”
第一种特质构造顺序(声明类的同时混入特质)
1)调用当前类的超类构造器
2)第一个特质的父特质构造器
3)第一个特质构造器
4)第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
5)第二个特质构造器
6)…重复4,5的步骤(如果有第3个,第4个特质)
7)当前类构造器 [案例演示]
第2种特质构造顺序(在构建对象时,动态混入特质)
1)调用当前类的超类构造
2)当前类构造
3)第一个特质构造器的父特质构造器
4)第一个特质构造器.
5)第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
6)第二个特质构造器
7)…重复5,6的步骤(如果有第3个,第4个特质)
8)当前类构造器 [案例演示]
分析两种方式对构造顺序的影响
第1种方式实际是构建类对象, 在混入特质时,该对象还没有创建。
第2种方式实际是构造匿名子类,可以理解成在混入特质时,对象已经创建了
案例的代码
object TraitCreateSeq {
def main(args: Array[String]): Unit = {
//分析
//1.E...
//2. A...
//3. B....
// 4. C....
// 5. D....
// 6. F....
val ff = new FF
//1. E...
//2. K....
//3. A
//4. B
//5. C
//6. D
println("第2种构建顺序...")
val ff2 = new KK with CC with DD
}
}
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....")
}
8.5.6扩展类的特质
特质可以继承类,以用来拓展该类的一些功能
trait LoggedException extends Exception{
def log(): Unit ={
println(getMessage()) // 方法来自于Exception类
}
}
所有混入该特质的类,会自动成为那个特质所继承的超类的子类
trait LoggedException extends Exception{
def log(): Unit ={
println(getMessage()) // 方法来自于Exception类
}
}
//UnhappyException 就是Exception的子类.
class UnhappyException extends LoggedException{
// 已经是Exception的子类了,所以可以重写方法
override def getMessage = "错误消息!"
}
如果混入该特质的类,已经继承了另一个类(A类),则要求A类是特质超类的子类,否则就会出现了多继承现象,发生错误
//扩展类特质
object TraitExtendsClass {
def main(args: Array[String]): Unit = {
println("hello")
}
}
trait LoggedException extends Exception{
def log(): Unit ={
println(getMessage()) // 方法来自于Exception类
}
}
//ok
//IndexOutOfBoundsException 和 LoggedException 都是Exception
class UnhappyException2 extends IndexOutOfBoundsException with LoggedException
class CCC
class UnhappyException3 extends CCC with LoggedException
8.5.7自身类型
说明
自身类型(self-type):主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况下,依然可以做到限制混入该特质的类的类型。
应用案例
举例说明自身类型特质,以及如何使用自身类型特质
代码
object SelfTypeDemo {
def main(args: Array[String]): Unit = {
}
}
//Logger就是自身类型特质
trait Logger {
// 明确告诉编译器,我就是Exception,如果没有这句话,下面的getMessage不能调用
this: Exception =>
def log(): Unit ={
// 既然我就是Exception, 那么就可以调用其中的方法
println(getMessage)
}
}
//因为 Logger 使用自身类型,要求继承(混入)Logger 必须是Exception子类
// class AAA extends Logger [错误]
class BBB extends Exception with Logger //ok
8.6嵌套类
8.6.1基本介绍
在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。如在类中可以再定义一个类,这样的类是嵌套类,其他语法结构也是一样。
嵌套类类似于Java中的内部类。
8.6.2Java内部类的简单回顾
在Java中,一个类的内部又完整的嵌套了另一个完整的类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类。内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系 【完整详细的回顾看我以前授课视频】
8.6.3Java内部类基本语法
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
8.6.4Java内部类的分类
从定义在外部类的成员位置上来看,
- 成员内部类(没用static修饰)
- 和静态内部类(使用static修饰),
定义在外部类局部位置上(比如方法内)来看:
分为局部内部类(有类名)
匿名内部类(没有类名)
这里我们就回顾一下成员内部类和静态内部类。
8.6.5Java内部类回顾案例
public class JavaInnerClass {
public static void main(String[] args) {
//创建一个外部类对象
OuterClass outer1 = new OuterClass();
//创建一个外部类对象
OuterClass outer2 = new OuterClass();
// 创建Java成员内部类
// 说明在Java中,将成员内部类当做一个属性,因此使用下面的方式来创建 outer1.new InnerClass().
OuterClass.InnerClass inner1 = outer1.new InnerClass();
OuterClass.InnerClass inner2 = outer2.new InnerClass();
//下面的方法调用说明在java中,内部类只和类型相关,也就是说,只要是
//OuterClass.InnerClass 类型的对象就可以传给 形参 InnerClass ic
inner1.test(inner2);
inner1.test(inner1);
inner2.test(inner1);
inner2.test(inner2);
// 创建Java静态内部类
// 因为在java中静态内部类是和类相关的,使用 new OuterClass.StaticInnerClass()
OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
}
}
class OuterClass { //外部类
class InnerClass { //成员内部类
public void test(InnerClass ic) { //test方法可以接收InnerClass变量
System.out.println(ic);
}
}
static class StaticInnerClass { //静态内部类
}
}
内部类的四个作用
8.6.6Scala嵌套类的使用1
1)请编写程序,定义Scala 的成员内部类和静态内部类,并创建相应的对象实例。
2)代码如下:
object ScalaInnerClass {
def main(args: Array[String]): Unit = {
//创建两个外部类对象
val outer1: ScalaOuterClass = new ScalaOuterClass()
val outer2: ScalaOuterClass = new ScalaOuterClass()
//创建内部类对象
val innerClass1 = new outer1.ScalaInnerClass
val innerClass2 = new outer2.ScalaInnerClass
//观察innerClass1 类型是 outer1.ScalaInnerClass
//innerClass2 类型是 outer2.ScalaInnerClass
println(innerClass1)
println(innerClass2)
//调用内部类的info方法,来访问外部类的属性(方法)
innerClass1.info()
innerClass2.info()
//创建一个静态内部类的对象实例
//创建静态内部类对象
val staticInner = new ScalaOuterClass.ScalaStaticInnerClass()
println(staticInner)
}
}
//外部类
class ScalaOuterClass {
var name : String = "scott"
private var sal : Double = 1.2
private def sayHi(): Unit = {
println("say hi~~~")
}
class ScalaInnerClass { //成员内部类
def info() = {
// 访问方式:外部类名.this.属性名
// 怎么理解 ScalaOuterClass.this 就相当于是 ScalaOuterClass 这个外部类的一个实例,
// 然后通过 ScalaOuterClass.this 实例对象去访问 name 属性
// 只是这种写法比较特别,学习java的同学可能更容易理解 ScalaOuterClass.class 的写法.
println("name = " + ScalaOuterClass.this.name
+ " age =" + ScalaOuterClass.this.sal)
//可以访问方法
ScalaOuterClass.this.sayHi
}
}
}
object ScalaOuterClass { //伴生对象
class ScalaStaticInnerClass { //静态内部类
}
}
8.6.7Scala嵌套类的使用2
请编写程序,在内部类中访问外部类的属性和方法两种方法。
方式1
内部类如果想要访问外部类的属性,可以通过外部类对象访问。
即:访问方式:外部类名.this.属性名
方式2
内部类如果想要访问外部类的属性,也可以通过外部类别名访问(推荐)。
即:访问方式:外部类名别名.属性名 【外部类名.this 等价 外部类名别名】
代码如下:
object ScalaInnerClass2 {
def main(args: Array[String]): Unit = {
//创建两个外部类对象
val outer1: ScalaOuterClass2 = new ScalaOuterClass2()
val outer2: ScalaOuterClass2 = new ScalaOuterClass2()
//创建内部类对象
val innerClass1 = new outer1.ScalaInnerClass2
val innerClass2 = new outer2.ScalaInnerClass2
//观察innerClass1 类型是 outer1.ScalaInnerClass
//innerClass2 类型是 outer2.ScalaInnerClass
println(innerClass1)
println(innerClass2)
//调用内部类的info方法,来访问外部类的属性(方法)
innerClass1.info()
innerClass2.info()
//创建一个静态内部类的对象实例
//创建静态内部类对象
val staticInner = new ScalaOuterClass.ScalaStaticInnerClass()
println(staticInner)
}
}
//外部类
//内部类如果想要访问外部类的属性,也可以通过外部类别名访问(推荐)。
即:访问方式:外部类名别名.属性名
class ScalaOuterClass2 {
myouter =>
class ScalaInnerClass2 { //成员内部类
def info() = {
//使用别名的方式来访问外部类的属性和方法,相当于myouter是一个外部类的实例ScalaOuterClass2.this
//这时需要将外部类的属性和方法的定义/声明放在别名后
println("name = " + myouter.name
+ " age =" + myouter.sal)
//可以访问方法
myouter.sayHi
}
}
var name: String = "scott"
private var sal: Double = 1.2
private def sayHi(): Unit = {
println("say hi~~~哈哈~~~")
}
}
object ScalaOuterClass2 { //伴生对象
class ScalaStaticInnerClass2 { //静态内部类
}
}
8.6.8类型投影
先看一段代码,引出类型投影
class ScalaOuterClass3 {
myOuter =>
class ScalaInnerClass3 { //成员内部类
def test(ic: ScalaInnerClass3): Unit = { //如果这样写,不同的外部对象创建的内部实例,是有匹配问题
System.out.println(ic)
}
}
}
解决方式-使用类型投影
类型投影是指:在方法声明上,如果使用 外部类#内部类 的方式,表示忽略内部类的对象关系,等同于Java中内部类的语法操作,我们将这种方式称之为 类型投影(即:忽略对象的创建方式,只考虑类型)
class ScalaOuterClass3 {
myOuter =>
class ScalaInnerClass3 { //成员内部类
def test(ic: ScalaOuterClass3#ScalaInnerClass3): Unit = { //如果这样写,不同的外部对象创建的内部实例,是有匹配问题
System.out.println(ic)
}
}
}