简介
本篇博客主要是介绍Kotlin语法中的【类和继承】相关的知识,帮助各位更快的掌握Kotlin,如果有疏漏或者错误,请在留言中指正,谢谢。 系列汇总地址
类
声明类
Kotlin中使用关键词class 声明类和java中保持一致,代码如下:
class Demo{ //class + 类名
}
类的构成=class +类名+类头(可选)+类体(可选),如果没有类体,括号也可省略
class Demo //class + 类名
主构造函数
在Kotlin的类中可以有一个主构造函数和多个次构造函数。我们看看什么样的算主构造函数,例子如下:
class Demo(){
}
看了例子估计心里开始有疑惑了,这和上面的例子有什么不同??仔细一看还真有不同,多了"()",这就声明了一个无参的主构造函数,当然你也可以声明有参数的,如下
class Demo(name: String){
}
主构造函数只是类头的一部分,还有一个可选关键词--constructor,当主构造函数想声明为非public 类型的时候,需要使用该关键词其他时候可以省略,例如:
class Demo private constructor(name: String){
}
这种情况一般是在写单例的时候会用到。
构造函数都有了,我们可以愉快的做些初始化了,但是但是要注意,主构造函数内不能包含任何代码。 这个时候我们需要使用另一个关键词 init
,使用init
代码块进行初始化代码即可,如下:
class Demo private constructor(name: String) {
init {//init代码块
println("测试输出")
}
}
上面的代码中name
可以直接使用在 init
代码块内和类体内声明的属性初始化中使用,只有这两种情况,代码如下
class Demo (name: String) {
init {
println("测试输出"+name) //在此处使用可以
}
var nameTest=name //此处给nameTest赋值可以,但不可以单独使用
}
当然我们可以让上面的name
有更多的用途怎么办呢?可以这样写:
class Demo(var name: String) { //此处加入 var关键词
init {
println("测试输出" + name)
}
var nameTest = name
fun printTest() { //方法内也可使用了
println(name)
}
}
不仅可以使用var
关键词,还可以使用val,具体的差别之前我们说过,再次不再赘述。
使用该关键词后,name就等同于是该类的成员变量,就等同于你直接声明在类中。
对于Kotlin来说还有个好用的地方,就是你声明的类中变量不需要写get/set方法,默认就会有,可以直接用。
次构造函数
Kotlin中类也可以声明前缀有 constructor 的次构造函数,如下:
class Demo { //有个默认的无参主构造函数
constructor(){// 有个无参次构造函数
}
}
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可。
下面根据主构造函数是否重写来分别讲解,先说未重写:
class A{
}
如何写它的次构造函数呢?
class A {
init {
println("--A主构造--")
}
constructor(name: String) { //此处屏蔽了 默认的主构造函数
println("--A次构造--")
}
}
上面的写法会导致Kotlin的默认无参主构造函数不可调用, 也就导致constructor(name: String):this()
失效,但我们上面说过必须要委托给主构造函数,这怎么办?其实Kotlin内部仍然会走默认的主构造函数,也就是说如果是默认主构造是可以省略不写的,有的时候是想写也没法写。
得到上面的结论是源于一个测试,代码如下:
var a = A("name"); //此处已经无法调用无参的,也就是默认的主构造失效,内部使用this() 也是不可以的
后面的打印结果
--A主构造-- //仍然走了主构造函数,此时只有默认主构造函数
--A次构造-- //然后再走次构造
所以得到上面的结论,在此佐证。 后续用实际代码详解,此处挖坑。
有的筒子可能就会想了,如果我们仍然想要一个无参数的构造函数怎么写?
class A {
init {
println("--A主构造--")
}
constructor(name: String) {
println("--A次构造--")
}
constructor(){ //增加一个无参的次构造函数
}
}
还有另一个方式,就是重写主构造函数,这两种方法都类似 java中自己重写一个无参的构造函数,现在就是区分了主、次构造函数的重写。还需强调的是主构造如果重写了,次构造不允许再重写。下面写个重写主构造函数的,后面细说:
class A() { //重写此处
init {
println("--A主构造--")
}
constructor(name: String) : this() {
println("--A次构造--")
}
// constructor(){ //次构造不允许使用
//
// }
}
下面我们讲一下重写主构造函数的
如下:
class Demo() { //没参数的主构造函数
constructor(name: String) : this() { //次构造函数委托给
}
}
此处就和默认的不一样了,这个地方需要使用this(),因为你重写了主构造函数。如果不重写是可以不写this()
通过上方的各种对比和例子我们对类的构造函数有了大概的了解,我们为了方便理解在这里总结一波,还有不明白的多看看例子:
类只有一个主构造函数,但可以有一个或多个次构造函数
次构造函数需要委托给主构造函数,可以通过直接或者间接的方式
在声明期间,以主构造函数为主,如果主构造函数重写了对应的构造方法,次构造函数不能用重复的出现对应的构造函数,上面有例子说明。
在调用期间,以次构造函数为主,如果未重写主构造函数,默认会有一个无参的主构造函数,如果重写一个无参次构造函数,则可调用的只有次构造函数。
创建类的实例
要创建一个类的实例,我们就像普通函数一样调用构造函数:
val invoice = Invoice()
val customer = Customer("Joe Smith")
//注意 Kotlin 并没有 new 关键字。
继承
在 Kotlin 中所有类都有一个共同的超类 Any,这对于没有超类型声明的类是默认超类:
class Example // 从 Any 隐式继承
Any 不是 java.lang.Object;尤其是,它除了 equals()、hashCode()和toString()外没有任何成员。 更多细节请查阅Java互操作性部分。
要声明一个显式的超类型,我们把类型放到类头的冒号之后:
open class A(p: Int) //注意此处的 open 关键词
class B(p: Int) : Base(p) //使用" :"
对于继承的类来说,它仍需满足之前类中主次构造函数的规定,还需满足继承类委托给被继承类的主构造函数(保证对应类的init模块都能运行的关键),此处的被委托的主构造函数如果是无参构造函数,可以省略不写
下面我们分两种类型去分别讲解,首先是未修改默认的主构造函数的情况:
我们先看一下被继承类:
open class BB { //未重写默主构造
constructor(name: String) {
println(name + "---BB")
}
constructor() {
println(".....---BB")
}
}
继承类:
class testA : BB { //此处testA也没重写主构造
constructor(name: String) : super(name) {//-- 1)
println("A---" + name)
}
constructor() {//-- 2)
println("A---" + "ss")
}
}
位置1处的构造函数,因为未重写主构造,所以本类的委托完成,所以只需委托父类即可,即可满足上面说的要求。
位置2处的构造函数,因为未重写主构造,所以本类的委托完成,又因为默认是委托父类的无参主构造函数(系统默认的或者重写的都满足),所以满足上面说的要求。
我们做下改动,将B 改动如下:
open class BB() {//重写默认主构造
constructor(name: String):this() { //手动委托主构造
println(name + "---BB")
}
// constructor() { //根据主次函数关系,此处无法存在
// println(".....---BB")
//
// }
}
这时候我们的继承类如何写呢?
class testA : BB {
constructor(name: String) : super(name) {
println("A---" + name)
}
constructor() {
println("A---" + "ss")
}
}
可以看到代码是没变化的,也就是说,只要是被继承类的主构造函数,是无参的,无论是默认还是重写都可以省略不写,当然也可以写上
class testA : BB {
constructor(name: String) : super(name) {//可以选择不同的父类的主构造函数进行委托
println("A---" + name)
}
constructor():super() {//无参的委托
println("A---" + "ss")
}
}
下面我们将一下修改默认主构造函数的情况
class testB(name: String) : BB(name) { //此处testB重写了默认主构造,所以此处B一定需要用主构造函数(直接或间接的)
init {
println(name + "99ss...---B")
}
constructor(name: String, age: Int) : this(name) {
println(name + "...---B")
}
}
也可以这样写:
class testB(name: String) : BB() { //此处必须有"()"
init {
println(name + "99ss...---B")
}
constructor(name: String, age: Int) : this(name) { //此处不能使用super了,不能重复委托父类,主构造函数已经委托过了
println(name + "...---B")
}
}
下面我们总结一波:
如果继承的类重写了默认主构造函数,此时必须用基类的主构造函数初始化(直接或间接)
如果继承的类没重写默认的主构造函数,此时可以使用super关键词初始化,我们也说过,未重写的的默认主构造函数,次构造函数都会委托给它,所以本类内也满足了委托条件,对于类外,也委托了被继承类的主构造方法,完成了两个类的条件
总结
至此已经学完了Kotlin的类和继承相关的知识,多回顾多思考,继续后续内容。