回顾下Java的静态概念
public static 返回值类型 方法名(参数列表) {方法体}静态属性...
说明:
Java中静态方法并不是通过对象调用的,而是通过类对象调用的,所以静态操作并不是面向对象的。
Scala语言是完全面向对象(万物皆对象)的语言,所以并没有静态的操作(即在Scala中没有静态
的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模
拟类对象,我们称之为类的“伴生对象”。这个类的所有静态内容都可以放置在它的伴生对象中声明
和调用。
伴生对象的示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest01 {def main(args: Array[String]): Unit = {ScalaPeople.sayHello() // 伴生对象直接调用伴生对象中的方法或者属性}}// 伴生类
class ScalaPeople {var name: String = _
}// 伴生对象
object ScalaPeople {var age: Int = _var sex: String = _def sayHello(): Unit = {println("Object ScalaPeople say Hello!!!")}
}
======================运行结果==============================
Object ScalaPeople say Hello!!!
======================运行结果==============================
伴生对象的小结
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.方法来实现调用;
9. 当一个文件中,存在伴生类和伴生对象时,文件的图标会发生变化。
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest02 {def main(args: Array[String]): Unit = {val c1 = new Child("白骨精")val c2 = new Child("豹子精")val c3 = new Child("蛇精")val c4 = new Child("孙悟空")Child.joinGame(c1)Child.joinGame(c2)Child.joinGame(c3)Child.joinGame(c4)Child.showNum()println("各自的任务如下:")Child.Work(c1)Child.Work(c2)Child.Work(c3)Child.Work(c4)}}class Child(inName: String) {var name = inName
}object Child {var childs_total: Int = 0def joinGame(child: Child): Unit = {println(child.name + " 加入了校外的游戏活动!!!")childs_total += 1}def showNum(): Unit = {println(s"当前游戏的人数已经有$childs_total" + "人参加了!!!")}def Work(child: Child): Unit = {if (child.name == "孙悟空") {println(child.name + " 过来参加打妖怪的!!!")} else {println(child.name + " 过来参加抓唐僧吃肉的!!!")}}}
======================运行结果==============================
白骨精 加入了校外的游戏活动!!!
豹子精 加入了校外的游戏活动!!!
蛇精 加入了校外的游戏活动!!!
孙悟空 加入了校外的游戏活动!!!
当前游戏的人数已经有4人参加了!!!
各自的任务如下:
白骨精 过来参加抓唐僧吃肉的!!!
豹子精 过来参加抓唐僧吃肉的!!!
蛇精 过来参加抓唐僧吃肉的!!!
孙悟空 过来参加打妖怪的!!!
======================运行结果==============================
在伴生对象中定义apply方法,可以实现: 类名(参数) 方式来创建对象实例.。
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest03 {def main(args: Array[String]): Unit = {// 通过“类名(参数)【Pig("宠物猪")】”创建对象。val pig01 = Pig("野猪")val pig02 = Pig()val pig03 = Pig("宠物猪")Pig.play(pig01)Pig.play(pig02)Pig.play(pig03)}}class Pig(inName: String) {var name = inName}object Pig {// 编写apply方法def apply(inName: String): Pig = new Pig(inName)def apply(): Pig = new Pig("识别不了的猪猪!")def play(pig: Pig): Unit = {printf("%s 在洗涮刷!!!n", pig.name)}
}
======================运行结果==============================
野猪 在洗涮刷!!!
识别不了的猪猪! 在洗涮刷!!!
宠物猪 在洗涮刷!!!
======================运行结果==============================
回顾Java接口
声明接口语法:interface 接口名实现接口语法:class 类名 implements 接口名1,接口21. 在Java中, 一个类可以实现多个接口;
2. 在Java中,接口之间支持多继承;
3. 接口中属性都是常量;
4. 接口中的方法都是抽象的。
1. 从面向对象来看接口并不属于面向对象的范畴,Scala是纯面向对象的语言,在Scala中没有接口;
2. Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特征(特
征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。 理解trait 等价于(interface +
abstract class)
trait 特质名 {trait体}trait 命名 一般首字母大写,例如:Cloneable , Serializable。object T1 extends Serializable {}Serializable: 就是scala的一个特质(在Java中Serializable是一个接口);在scala中,java中的接口可以当做特质使用。
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在
使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接没有父类:class 类名 extends 特质1 with 特质2 with 特质3 ..有父类;class 类名 extends 父类 with 特质1 with 特质2 with 特质3
可以把特质可以看作是对继承的一种补充
Scala的继承是单继承,也就是一个类最多只能有一个父类,这种单继承的机制可保证类的纯洁
性,比c++中的多继承机制简洁。但对子类功能的扩展有一定影响.所以,Scala引入trait特征第一
可以替代Java的接口, 第二个也是对单继承机制的一种补充。
如上图,实现该功能的示例代码如下:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest04 {def main(args: Array[String]): Unit = {val c1 = Conn()val e1 = Conn()}}// 定义一个Trait
trait getCEConnTrait {def getConn(): Unit = {println("获取对应的数据库连接!!!")}}class A {// to do
}class B extends A {// to do
}class C extends A with getCEConnTrait {override def getConn(): Unit = {println("获取到C数据库的连接!")}}class D {// to do
}class F extends D {// to do
}class E extends D with getCEConnTrait {override def getConn(): Unit = {println("获取到E数据库的连接!")}}
======================运行结果==============================
获取到C数据库的连接!
获取到E数据库的连接!
======================运行结果==============================
1. Scala提供了特质(trait),特质可以同时拥有抽象方法和具体方法,一个类可以实现/继承多
个特质;
2. 特质中没有实现的方法就是抽象方法。类通过extends继承特质,通过with可以继承多个特质;
3. 所有的java接口都可以当做Scala特质使用。
以上说明的示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest05 {def main(args: Array[String]): Unit = {val dog = new Dogdog.eat("素类、肉类!")dog.run()dog.Jiao()}}/*** 创建一个特质(trait): AnimalTrait* 特质可以同时拥有“抽象方法”和“具体方法”*/trait AnimalTrait {// 抽象方法def run()// 具体实现方法def eat(message: String): Unit = {println("这个动物吃:" + message)}}trait AnimalTrait02 {// 抽象方法def Jiao()}// 类通过extends继承特质,通过with可以继承多个特质
class Dog extends AnimalTrait with AnimalTrait02 {// 实现Trait中的抽象方法:run()和JIAO()override def run(): Unit = {println("狼狗一般跑的都比较快!")}override def Jiao(): Unit = {println("狼狗见到陌生人就汪汪叫!")}
}
======================运行结果==============================
这个动物吃:素类、肉类!
狼狗一般跑的都比较快!
狼狗见到陌生人就汪汪叫!
======================运行结果==============================
1. 除了可以在类声明时继承特质以外,还可以在构建对象时混入特质,扩展目标类的功能;
2. 此种方式也可以应用于对抽象类功能进行扩展;
3. 动态混入是Scala特有的方式(java没有动态混入),可在不修改类声明/定义的情况下,扩展类的
功能,非常的灵活,耦合性低 ;
4. 动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能。
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest06 {def main(args: Array[String]): Unit = {// 具体类构建对象china时混入特质Country,扩展目标类China的功能,// 同时需要实现特质中的抽象方法。// 同时也不会修改类声明/定义。val china = new China with Country {override def Stronger(name: String): Unit = {println(name + "非常强大,让百姓免受战火之苦!")}}china.area("中国", "960万平方公里")china.historyInfo("中国")// 抽象类构建对象american时混入特质Country,扩展目标类American的功能,// 同时需要实现抽象类中的抽象方法和特质中的抽象方法。// 同时也不会修改类声明/定义。val american = new American with Country {override def say(): Unit = {println("特朗普特别喜欢在推特上跟网友瞎砍!")}override def Stronger(name: String): Unit = {println(name + "特别蛮横,欺负弱小国家!遭到世界人民的谴责与愤怒!")}}american.Stronger("美国")american.say()// 动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能。val egypt = new Egyptegypt.historyInfo("埃及")}}trait Country {// 具体实现方法def area(country_name: String, size: String): Unit = {println(country_name + "的面积是:" + size)}def army(): Unit = {// to do}def Stronger(name: String)}class China {def historyInfo(name: String): Unit = {println(name + "是四大文明古国之一!")}}abstract class American {def say()}class Egypt extends China {// to do
}
======================运行结果==============================
中国的面积是:960万平方公里
中国是四大文明古国之一!
美国特别蛮横,欺负弱小国家!遭到世界人民的谴责与愤怒!
特朗普特别喜欢在推特上跟网友瞎砍!
埃及是四大文明古国之一!
======================运行结果==============================
在Scala中创建对象共有几种方式?
1. new 对象
2. apply 创建
3. 匿名子类方式
4. 动态混入
基本介绍
构建对象的同时如果混入多个特质,称之为叠加特质,那么特质声明顺序从左到右,方法执行
顺序从右到左。
分析叠加特质时,对象的构建顺序,和执行方法的顺序示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest07 {def main(args: Array[String]): Unit = {/*** 说明:* 创建对象mySQL01时,特质的声明顺序从左到右,即从DB01特质开始执行,待DB01执行完以后,* 再执行下一个特质最后到File01e特质* 即声明顺序:1. 第一先执行DB01特质,根据继承关系可以知道特质声明顺序:DB01 --> Data01 --> Operate01,* 先声明的先放入栈(先进后出的原则)中,然后再执行对应的特质。* 2. 第二执行File01特质,根据继承关系可以知道特质声明顺序:File01 --> Data01(由于已经在第一* 次声明的时候执行过了),所以此时特质声明顺序只有:File01,然后执行。** Scala在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行,特质中如果调用super,并不是表示调用父* 特质的方法,而是向前面(左边)继续查找特质,如果找不到,才会去父特质查找* 即方法执行顺序:1. 先执行特质File01中的insert方法,由于方法中调用了super关键字,所以执行到这里时会去找* 特质File01左边的特质DB01,找到对应的insert()方法(否则就去父类中特质查找对应的方法),* 特质DB01中的insert()方法执行之后,又遇到super关键字,所以此时会去找DB01左边的特质,此时* 发现没有,所以会查找父特质中的insert()方法,然后执行完就结束了。*/println("输出信息如下:")val mySQL01 = new MySQL01 with DB01 with File01println("------------------华丽的分隔符-------------------------")println("输出信息如下:")mySQL01.insert(10001)}}trait Operate01 {println("Operate01执行......")def insert(id: Int) // 抽象方法}// 特质也可以继承特质,同样也需要实现特质中的抽象方法
// 特质Data01继承Operate01
trait Data01 extends Operate01 {println("Data01执行......")override def insert(id: Int): Unit = {println("Data01插入数据:" + id)}}// 特质DB01继承Data01
trait DB01 extends Data01 {println("DB01执行......")override def insert(id: Int): Unit = {println("向数据库DB01插入数据:" + id)super.insert(id)}}// 特质File01 extends Data01
trait File01 extends Data01 {println("File01执行......")override def insert(id: Int): Unit = {println("向文件File01写入数据:" + id)super.insert(id) // 调用了insert方法(难点),这里super动态混入时不一定是父类/*** 说明:* 如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛型必须* 是该特质的直接超类类型,例如:super[Data01].insert(id)* 此时如果执行到super关键字,就不会先找左边的特质了,而是直接找到当前指定* 泛型[特质],即当前特质的父特质。*/}}class MySQL01{// to do
}======================运行结果==============================
输出信息如下:
Operate01执行......
Data01执行......
DB01执行......
File01执行......
------------------华丽的分隔符-------------------------
输出信息如下:
向文件File01写入数据:10001
向数据库DB01插入数据:10001
Data01插入数据:10001
======================运行结果==============================
叠加特质注意事项和细节
1. 特质声明顺序从左到右;
2. Scala在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行;
3. Scala中特质中如果调用super,并不是表示调用父特质的方法,而是向前面(左边)继续查找
特质,如果找不到,才会去父特质查找;
4. 如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛型必须是该特质的直接
超类类型
在特质中重写抽象方法特例
trait Operate {def insert(id : Int)}trait File extends Operate {def insert( id : Int ): Unit = {println("将数据保存到文件中..")super.insert(id)}}
运行代码,并小结问题 (错误,原因就是没有完全的实现insert,同时你还没有声明abstract overrid)
解决问题
方式1 : 去掉 super()...
方式2: 调用父特质的抽象方法,那么在实际使用时,没有方法的具体实现,无法编译通过,为了避免这种情况的发生。可重写抽象方法,这样在使用时,就必须考虑动态混入的顺序问题。trait Operate02 {def insert(id : Int)}trait File02 extends Operate02 {abstract override def insert( id : Int ): Unit = {println("将数据保存到文件中..")super.insert(id)}}
理解 abstract override 的小技巧分享:
可以这里理解,当我们给某个方法增加了abstract override 后,就是明确的告诉编译器,该方法确
实是重写了父特质的抽象方法,但是重写后,该方法仍然是一个抽象方法(因为没有完全的实现,需
要其它特质继续实现[可以通过混入顺序实现])
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest08 {def main(args: Array[String]): Unit = {val mySQL02 = new MySQL02 with DB02 with File02 // okmySQL02.insert(10001)// val mySQL02_01 = new MySQL02 with File02 // error 直接就报错val mySQL02_02 = new MySQL02 with DB02 // okmySQL02_02.insert(10002)// val mySQL02_03 = new MySQL02 with File02 with DB02 // error// mySQL02_03.insert(10003)}}trait Operate02 {def insert(id: Int)
}trait File02 extends Operate02 {/*** @param id* 说明:* 1. 如果在子特质中重写/实现了一个父特质的抽象方法,但同时调用super关键字,* 这时的方法不是完全实现,因此需要声明为“abstract override”* 2. 这时super.insert(id)的调用就和动态混入顺序有密切关系。*/abstract override def insert(id: Int): Unit = {println("将数据文件(File02)保存到文件中:" + id)super.insert(id)}
}trait DB02 extends Operate02 { // 继承了Operate02,并实现了Operate02的insert(),其中override可以省略!def insert(id: Int): Unit = {println("将数据(DB)保存到数据库中:" + id)}
}class MySQL02 {// to do
}
======================运行结果==============================
将数据文件(File02)保存到文件中:10001
将数据(DB)保存到数据库中:10001
将数据(DB)保存到数据库中:10002
======================运行结果==============================
富接口:即该特质中既有抽象方法,又有非抽象方法,比如:trait Operate {def insert( id : Int ) //抽象def pageQuery(pageno:Int, pagesize:Int): Unit = { //实现println("分页查询")}}
特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。混入该
特质的类就具有了该字段,字段不是继承,而是直接加入类,成为自己的字段。
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest09 {def main(args: Array[String]): Unit = {val p3 = new People03 with Operate03 {override var age: Int = _ // 重写字段属性}// p3对象就拥有了age这个字段属性p3.age = 12print(p3.age) // 12}}trait Operate03 {var name: String =_var age: Int // 特质中未被初始化的字段在具体的子类中必须被重写。
}class People03 {// to do
}
介绍
特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成。具体实现参
考“特质叠加”内容。
第一种特质构造顺序(声明类的同时混入特质)
1. 调用当前类的超类构造器;
2. 第一个特质的父特质构造器;
3. 第一个特质构造器;
4. 第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
5. 第二个特质构造器;
6. .......重复4,5的步骤(如果有第3个,第4个特质);
7. 当前类构造器。
第二种特质构造顺序(在构建对象时,动态混入特质)
1. 调用当前类的超类构造器;
2. 当前类构造器;
3. 第一个特质构造器的父特质构造器;
4. 第一个特质构造器.;
5. 第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行;
6. 第二个特质构造器;
7. .......重复5,6的步骤(如果有第3个,第4个特质);
8. 当前类构造器
分析两种方式对构造顺序的影响
第一种方式实际是构建类对象, 在混入特质时,该对象还没有创建。
第二种方式实际是构造匿名子类,可以理解成在混入特质时,对象已经创建了。
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest10 {def main(args: Array[String]): Unit = {/*** 直接构建类: 声明类的同时混入特质(特质构造顺序)* 1. 调用当前类的超类构造器;* 2. 第一个特质的父特质构造器;* 3. 第一个特质构造器;* 4. 第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行* 5. 第二个特质构造器;* 6. .......重复4,5的步骤(如果有第3个,第4个特质);* 7. 当前类构造器。* 特点:实际是构建类对象,在混入特质时,该对象还没有创建,最后(执行第7步骤)才会创建。*/val F = new FFprintln("------------------华丽分隔符-------------------")/*** 在构建对象时,动态混入特质(特质构造顺序)* 1. 调用当前类的超类构造器;* 2. 当前类构造器;* 3. 第一个特质构造器的父特质构造器;* 4. 第一个特质构造器.;* 5. 第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行;* 6. 第二个特质构造器;* 7. .......重复5,6的步骤(如果有第3个,第4个特质);* 8. 当前类构造器* 特点:实际是构造匿名子类,可以理解成在混入特质时,对象已经创建了(即执行第2步骤的时候就创建了)。*/val K = new KK with CC with DD}}trait AA {println(")
}trait BB extends AA {println(")
}trait CC extends BB {println(")
}trait DD extends BB {println(")
}class EE {println(")
}// 声明类的同时混入特质
class FF extends EE with CC with DD {println(")
}class KK extends EE {println(")
}
======================运行结果==============================
EE...
AA...
BB....
CC....
DD....
FF....
------------------华丽分隔符-------------------
EE...
KK....
AA...
BB....
CC....
DD....
======================运行结果==============================
1. 特质可以继承类,以用来拓展该类的一些功能;// 特质LoggedException继承了Exception类,扩展了改特质的功能(即增加了log方法等。)trait LoggedException extends Exception{def log(): Unit ={println(getMessage()) // 方法来自于Exception类}}2. 所有混入该特质的类,会自动成为那个特质所继承的超类的子类;trait LoggedException extends Exception{def log(): Unit ={println(getMessage()) // 方法来自于Exception类}}// 由于特质LoggedException继承了Exception类,所以此时UnhappyException类继承特质// LoggedException,因此此时的UnhappyException就自动成为了Exception的子类。class UnhappyException extends LoggedException{// UnhappyException已经是Exception的子类了,所以可以重写方法override def getMessage = "错误消息!"}3. 如果混入该特质的类,已经继承了另一个类(A类),则要求A类是特质超类的子类,否则就会出现
了多继承现象,发生错误。trait LoggedException extends Exception{def log(): Unit ={println(getMessage()) // 方法来自于Exception类}}// 因为IdexOutOfBoundsException类是特质LoggedException超类Exception的子类,所以如下// 特质的混入不会出错。class UnhappyException extends IdexOutOfBoundsException with LoggedException {// 已经是Exception的子类了,所以可以重写方法override def getMessage = "错误消息!"}class AA {}// 因为AA类不是特质LoggedException超类Exception的子类,所以如下特质混入会出错。class UnhappyException extends AA with LoggedException {// 已经是Exception的子类了,所以可以重写方法override def getMessage = "错误消息!"}
说明
自身类型:主要是为了解决特质的循环依赖问题,同时可以确保特质在不扩展某个类的情况
下,依然可以做到限制混入该特质的类的类型。
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest11 {def main(args: Array[String]): Unit = {val logInfo = new LogInfologInfo.log("这段英语翻译有误!")}}class Exception {def getMessage(): Unit = {println("Exception info ......")}}trait Logger {// 明确告诉编译器,我就是Exception,如果没有这句话,下面的getMessage不能调用this: Exception =>def log(info: String): Unit = {// 既然我就是Exception, 那么就可以调用其中的方法println("调用方法getMessage:")getMessage()println("Logger打印信息:" + info)}
}// 自身类型的使用, 这种用法是√
class LogInfo extends Exception with Logger {// to do
}/*
// 自身类型的使用, 这种用法是×
//
class LogInfo extends Logger {// to do
}*/
======================运行结果==============================
调用方法getMessage:
Exception info ......
Logger打印信息:这段英语翻译有误!
======================运行结果==============================
基本介绍
在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。如在类中可以再定义一个类,这样
的类是嵌套类,其他语法结构也是一样。嵌套类类似于Java中的内部类。
Java内部类的简单回顾
在Java中,一个类的内部又完整的嵌套了另一个完整的类结构。被嵌套的类称为内部类(inner
class),嵌套其他类的类称为外部类。内部类最大的特点就是可以直接访问私有属性,并且可以体
现类与类之间的包含关系
Java内部类基本语法
class Outer{ //外部类class Inner{ //内部类}}class Other{ //外部其他类}
Java内部类的分类
从定义在外部类的成员位置上来看,1. 成员内部类(没用static修饰)2. 静态内部类(使用static修饰),定义在外部类局部位置上(比如方法内)来看:分为局部内部类(有类名)匿名内部类(没有类名)
示例代码:
package com.lj.scala.AccompanyObject;/*** @author Administrator* @create 2020-03-11*/
public class Test {public static void main(String[] args) {//创建一个外部类对象OuterClass outer1 = new OuterClass();//创建另一个外部类对象OuterClass outer2 = new OuterClass();// 创建Java成员内部类// 说明在Java中,将成员内部类当做一个属性,因此使用下面的方式来创建 w InnerClass().OuterClass.InnerClass inner1 = w InnerClass();OuterClass.InnerClass inner2 = w InnerClass();//下面的方法调用说明在java中,内部类只和类型相关,也就是说,只要是//OuterClass.InnerClass 类型的对象就可以传给形参 st(inner2);st(inner1);// 创建Java静态内部类// 因为在java中静态内部类是和类相关的,使用 new OuterClass.StaticInnerClass()OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();}
}class OuterClass { //外部类class InnerClass { //成员内部类public void test( InnerClass ic ) {System.out.println(ic);}}static class StaticInnerClass { //静态内部类}}
Scala嵌套类的使用
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest12 {def main(args: Array[String]): Unit = {val outer1 : ScalaOuterClass = new ScalaOuterClass();val outer2 : ScalaOuterClass = new ScalaOuterClass();// Scala创建内部类的方式和Java不一样,将new关键字放置在前,使用:对象.内部类的方式创建。val inner1 = new outer1.ScalaInnerClass()val inner2 = new outer2.ScalaInnerClass()//创建静态内部类对象val staticInner = new ScalaOuterClass.ScalaStaticInnerClass()println(staticInner)}}class ScalaOuterClass { //伴生类class ScalaInnerClass { //成员内部类}}object ScalaOuterClass { //伴生对象class ScalaStaticInnerClass { //静态内部类}}
《方式一》
内部类如果想要访问外部类的属性,可以通过外部类对象访问。即(访问方式):外部类名.this.属性名。
《方式2》
内部类如果想要访问外部类的属性,也可以通过外部类别名访问(推荐)。即(访问方式):外部类名别
名.属性名 【外部类名.this 等价 外部类名别名】
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest12 {def main(args: Array[String]): Unit = {val outer1 : ScalaOuterClass = new ScalaOuterClass();val outer2 : ScalaOuterClass = new ScalaOuterClass();// Scala创建内部类的方式和Java不一样,将new关键字放置在前,使用:对象.内部类的方式创建。val inner1 = new outer1.ScalaInnerClass()val inner2 = new outer2.ScalaInnerClass()outer1.name = "Jack"outer1.setSal(8800.59)inner1.info()outer2.name = "Tom"outer2.setSal(25888.29)inner2.info2()//创建静态内部类对象val staticInner = new ScalaOuterClass.ScalaStaticInnerClass()}}class ScalaOuterClass { //伴生类myOuter => //这样写,可以理解成myOuter就是代表外部类的一个对象。// 当给外部指定别名时,需要将外部类的属性放到别名后。var name:String = _private var sal: Double = _class ScalaInnerClass { //成员内部类// 内部类访问外部类的属性《方式一》def info(): Unit = {// 访问方式:外部类名.this.属性名// 怎么理解 ScalaOuterClass.this 就相当于是 ScalaOuterClass 这个外部类的一个实例,// 然后通过 ScalaOuterClass.this 实例对象去访问 name 属性println("不是别名方式:name = " + ScalaOuterClass.this.name + " sal =" + ScalaOuterClass.this.sal)}def info2(): Unit = {println("别名方式:name = " + myOuter.name + " sal =" + myOuter.sal)}}def setSal(inSal: Double): Unit = {this.sal = inSal}}object ScalaOuterClass { //伴生对象class ScalaStaticInnerClass { //静态内部类}}
======================运行结果==============================
不是别名方式:name = Jack sal =8800.59
别名方式:name = Tom sal =25888.29
======================运行结果==============================
解决方式-使用类型投影
类型投影是指:在方法声明上,如果使用“外部类#内部类”的方式,表示忽略内部类的对象关系,
等同于Java中内部类的语法操作,我们将这种方式称之为类型投影(即:忽略对象的创建方式,只考
虑类型)。
示例代码:
package com.lj.scala.AccompanyObject/*** @author Administrator* @create 2020-03-11*/
object AmpObjTest13 {def main(args: Array[String]): Unit = {val outer1 : ScalaOuterClass01 = new ScalaOuterClass01();val outer2 : ScalaOuterClass01 = new ScalaOuterClass01();val inner1 = new outer1.ScalaInnerClass01()val inner2 = new outer2.ScalaInnerClass01()/*** 说明下面调用test的正确和错误的原因:* 1. Java中的内部类从属于外部类,因此在java中st(inner2)就可以,因为是按类型来匹配的。* 2. Scala中内部类从属于外部类的对象,所以外部类的对象不一样,创建出来的内部类也不一样,无法互换使用;* 3. 比如你使用ideal看一下在st()的形参上,它提示的类型是outer1.ScalaOuterClass,而不是ScalaOuterClass。*/st(inner1) // ok, 因为需要outer1.ScalaInnerClass01// st(inner2) // error, 需要outer1.ScalanInner01 而不是 st(inner2) // ok 因为需要outer2.ScalaInnerClass01// 解决使用类型投影st2(inner1) // st2(inner2) // ok}}class ScalaOuterClass01 {myOuter =>class ScalaInnerClass01 { //成员内部类def test(ic: ScalaInnerClass01): Unit = {println(ic)}/*** 解决使用类型投影:* 类型投影是指:在方法声明上,如果使用“外部类#内部类”的方式,表示忽略内部类的对象关系,* 等同于Java中内部类的语法操作,我们将这种方式称之为类型投影(即:忽略对象的创建方式,只考* 虑类型)。*/def test2(ic: ScalaOuterClass01#ScalaInnerClass01): Unit = {println(ic)}}
}
对以前的知识回顾,加深基础知识!
学习来自:北京尚硅谷韩顺平老师—尚硅谷大数据技术之Scala
每天进步一点点,也许某一天你也会变得那么渺小!!!
本文发布于:2024-01-28 06:12:19,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17063935435375.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |