伴生类和伴生对象、样例类
scala中没有static关键字,不支持创建静态属性和方法。
scala中采取的方案就是伴生类和伴生对象,两者名称一致,伴生类为class
,伴生对象为object
,可以理解伴生类就是java中定义的普通类,只是不能创建静态属性和函数。而伴生对象是一个单例对象,可以直接通过类名调用其中的属性和函数,使用效果和Java调用静态属性/方法类似
案例:
class School(nameIn: String, leaderIn: String, teacherNumIn: Int, addressIn: String) {
var schoolName: String = nameIn
var leaderName: String = leaderIn
var teacherNum: Int = teacherNumIn
var address: String = addressIn
def showInfo(): Unit = {
println(
"name:" + schoolName +
" leader:" + leaderName +
" teacherNum:" + teacherNum +
" address:" + address
)
}
}
object School {
def showInfo(): Unit = {
println("object constructor")
}
}
样例类会同时生成对应的伴生类和伴生对象,定义关键词为:case
例如:case class Student(name:String,age:Int)
构造函数
scala的构造函数分为主构造函数和辅助构造函数,主构造函数的参数列表定义在类名后面,辅助构造函数的函数名为this
// 这里是主构造函数的参数列表
class Person(nameIn: String, ageIn: Int) {
var name: String = nameIn
var age: Int = ageIn
// 这里是辅助构造函数
def this(){
this("张三",18)
}
}
- 一个类只能有一个主构造函数,可以拥有多个辅助构造函数(函数签名不重复即可)
- 辅助构造函数的第一行必须是
this()
方法 - 每个辅助构造函数都会直接或间接的调用主构造函数
- 当程序加载主构造函数时,会将类中代码执行一遍
伴生对象的apply方法
伴生对象的apply方法是用于创建类对象的
例如:
object Person{
def apply(nameIn: String, ageIn: Int): Person = new Person(nameIn, ageIn)
}
方法实际就是返回new产生的类对象,在主方法中通过Person.apply("zs",22)
方式也可以获得类对象,不过scala提供了更简便的写法Person("zs",22)
,两者效果相同
总结创建对象的方式:
object Test {
def main(args: Array[String]): Unit = {
// 这里获得的是School的伴生对象
val s1 = School
print("s1--->")
s1.showInfo()
// 这里是调用了School伴生对象的无参apply方法
val s2 = School()
print("s2--->")
s2.showInfo()
// 通过new关键字创建,调用了School伴生类的辅助构造方法,当使用无参构造时,可以省略括号
val s3 = new School
print("s3--->")
s3.showInfo()
// 通过new关键字创建,同样是调用了School伴生类的辅助构造方法
val s4 = new School()
print("s4--->")
s4.showInfo()
// 调用了School伴生对象的带参apply方法
val s5 = School("C_12", "王五", 53, "武汉")
print("s5--->")
s5.showInfo()
// 调用School伴生类的主构造方法
val s6 = new School("C_12", "王五", 53, "武汉")
print("s6--->")
s6.showInfo()
}
}
class School(nameIn: String, leaderIn: String, teacherNumIn: Int, addressIn: String) {
var schoolName: String = nameIn
var leaderName: String = leaderIn
var teacherNum: Int = teacherNumIn
var address: String = addressIn
def this() {
this("C_11", "张三", 12, "北京")
}
def showInfo(): Unit = {
println(
"name:" + schoolName +
" leader:" + leaderName +
" teacherNum:" + teacherNum +
" address:" + address
)
}
}
object School {
def apply(nameIn: String, leaderIn: String, teacherNumIn: Int, addressIn: String): School = new School(nameIn, leaderIn, teacherNumIn, addressIn)
def apply(): School = new School("C_13", "李四", 32, "重庆")
def showInfo(): Unit = {
println("object constructor")
}
}
输出结果
s1--->object constructor
s2--->name:C_13 leader:李四 teacherNum:32 address:重庆
s3--->name:C_11 leader:张三 teacherNum:12 address:北京
s4--->name:C_11 leader:张三 teacherNum:12 address:北京
s5--->name:C_12 leader:王五 teacherNum:53 address:武汉
s6--->name:C_12 leader:王五 teacherNum:53 address:武汉
特质trait和动态混入
特质,类似与Java接口
1、trait中未被实现的方法默认是抽象方法,不需要在方法前加abstract
2、子类实现trait,使用extends而不是implements
3、子类方法前不需要写override关键字
4、实现多个特质时,可以使用with关键字来添加其他特质
动态混入:
举例:val 对象名 = new 类名 with 特质名 [with 特质名]
注意事项:
-
即便类是空的,对象也可以调用特质中的方法,类可以是普通类也可以是抽象类
-
如果抽象类中有同名的方法,要先混入特质再实现方法(一般在最后使用匿名内部类一起实现)
-
同时混入多个特质,叫做叠加特质,那么特质声明顺序从左到右,方法执行顺序从右到左
-
动态混入创建对象过程
1、先检测混入的第一个对象(从左向右),如果它有父类(特质也当作类),将执行父类的加载,再执行本类加载 2、第一个对象加载完后,向右加载下一个特质,同样有父类先加载父类,但如果父类在之前加载过了,本次将不再重复加载
-
执行动态混入的方法,先执行右边的特质的方法
-
动态混入多个特质时,特质中的super指的时混入时,左边的特质,左边没有或者找不到才去执行父类
-
如果想要调用具体特质的方法,可以指定 super[特质].xxx()
-
这里标注的特质必须是当前类的直接超类
-
如果在子特质中重写/实现了一个父特质的抽象方法,但是又使用super调用这个方法,这时方法就不是完全实现,因此需要声明为abstract override
-
此时调用的super,在动态混入过程中,可能会指向左边的特质,此时或许就可以顺利运行