Kotlin 类与继承
Posted 星火燎原2016
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 类与继承相关的知识,希望对你有一定的参考价值。
类
kotlin 中使用关键字 class 声明类
类声明由类名,类头(指定其类型参数,主构造函数等),花括号包围的类体构成,类头 和类体都是可选的。
class Student
如果一个类没有类体,则可以省略花括号
class Empty
主构造函数
一个类可以有一个主构造函数和一个或多个次构造函数,主构造函数是类头的一部分,主构造函数跟在 类名后。
class 类名 constructor(形参1,形参2,形参3)
class Employee constructor(firstName: String, lastName: String, age: Int)
private var name: String
private var firstName: String
private var lastName: String
private var workTime: Int
init
println("first init")
this.firstName = firstName // 主构造函数不能包含任何初始化代码,所以放在 init 中初始化成员变量 firstName,lastName
this.lastName = lastName
this.name = this.firstName + this.lastName // 成员变量可以在 init 初始化代码块中初始化
workTime = age / 2
init
println("second init")
// 输出结果:
first init
second init
注意点:
- 关键字 constructor ,在 Java 中,构造方法名必须和类名相同,而在 kotlin 中,是通过 contructor 关键字来标明的,且对于主构造函数而言,它的位置是在 类头(即紧跟类名的后面),而不是在类体中。
- 关键字 init : init 被称作是初始化代码块,他的作用是为主构造函数服务的,由于主构造函数是放在 类头中的,是不能包含任何初始化语句的(这是语法规定的),这个时候就是 init 的用武之地了,我们可以把初始化执行语句放在 init 代码块中进行初始化,为类的成员属性赋值。
- 在实例初始化期间,初始化代码块按照他们出现在类体重的顺序执行,与属性初始化器交织在一起。
如果主构造函数没有任何注解或可见性修饰符,则可以省略这个 constructor 关键字。
class Employee(fristName: String, lastName: String, age: Int) // 省略 constructor 关键字
类的成员属性初始化不是必须放在 init 代码块中的,可以在定义属性时直接将主构造函数中的形参赋值给他。
class Employee constructor(firstName: String, lastName: String, age: Int)
private var name: String = firstName + lastName
private var firstName: String = firstName
private var lastName: String = lastName
private var workTime: Int = age / 2
init
println("first init")
this.firstName = firstName // 主构造函数不能包含任何初始化代码,所以放在 init 中初始化成员变量 firstName,lastName
this.lastName = lastName
this.name = this.firstName + this.lastName // 成员变量可以在 init 初始化代码块中初始化
workTime = age / 2
init
println("second init")
可以发现,这种在构造函数中声明形参,然后在属性定义进行赋值,有点繁琐,我们可以直接在主构造函数中定义类的属性。
class Computer(private var name: String, private var price: Int)
fun printInfo()
println("name = $name , price = $price")
fun main(args: Array<String>)
var appleComputer = Computer("apple", 10000)
appleComputer.printInfo()
输出结果:
name = apple , price = 10000
当我们在定义一个类时,如果没有显式的提供主构造函数,Kotlin 编译器会默认生成一个无参主构造函数,这点和 Java 是一样的
class Mouse
private var name: String = "LenovoMousc"
private var price: Int = 23
fun printInfo()
println("name = $name , price = $price")
fun main(args: Array<String>)
var lenovoMouse = Mouse()
lenovoMouse.printInfo()
输出结果:
name = LenovoMousc , price = 23
次构造函数
与主构造函数不同的是: 次构造函数是定义在类体中的,并且次构造函数可以有多个,而主构造函数只能有一个。
class MyButton : AppCompatButton
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.attr.buttonStyle)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
注意点:
- 可以使用 this 关键字来调用 自己的其他构造函数
- 使用 super 关键字来调用父类构造函数
我们看下,同时定义主构造函数和多个次构造函数
class Mouse(name: String, price: Int, address: String)
private var name: String = name
private var price: Int = price
private var address: String = address
constructor(name: String) : this(name, 0)
constructor(name: String, price: Int) : this(name, price, "")
fun printInfo()
println("name = $name , price = $price, address = $address")
fun main(args: Array<String>)
var lenovoMouse = Mouse("applemouse")
lenovoMouse.printInfo()
一个 三个参数的主构造函数,两个次构造函数。一个参数的次构造函数调用了两个参数的次构造函数,两个参数的次构造函数调用了三个参数的主构造函数。
我们把 两个参数的次构造函数的 this(name,price,"") 删除掉,会出现什么问题呢?
class Mouse(name: String, price: Int, address: String)
private var name: String = name
private var price: Int = price
private var address: String = address
constructor(name: String) : this(name, 0)
constructor(name: String, price: Int) // 删除掉调用三个参数的主构造器,会提示: Primary constructor call expected
fun printInfo()
println("name = $name , price = $price, address = $address")
fun main(args: Array<String>)
var lenovoMouse = Mouse("applemouse")
lenovoMouse.printInfo()
删除三个参数的主构造函数会提示: Primary constructor call expected
结论:次构造函数会直接或间接调用主构造函数的
创建类的实例
要创建一个类的实例,可以普通函数一样调用 构造函数。
kotlin 中没有 new 关键字
var employee = Employee("1001", "xiaohe", 12)
类成员
类可以包含:
- 构造函数与初始化代码块
- 函数
- 属性
- 嵌套类和内部类
- 对象声明
继承
在 kotlin 中所有类都有一个共同的超类 Any , 这对于没有声明超类型的类提供了默认的超类
注意点:
-
Kotlin 中如果类前面不加关键字 open, 则该类默认是 final 的 ,也就是不能被继承,如果继承该类,会报错: This type is final, so it cannot be inherited from.
-
Any 类 默认提供了三个方法 : equal() , hashCode() ,toString()
-
基类中没有定义构造器时,Kotlin 会默认添加一个无参的构造器,所以子类在继承该基类时,需要实现该基类的无参构造器。
// 编译器会提供的默认无参的构造器 open class Phone // 继承基类时,需要实现基类中的无参的构造器 class ApplePhone(private var rom: Int) : Phone() fun printInfo() println("the rom is $rom")
输出结果:
the rom is 4
-
在写子类是,如果子类中有主构造函数,那么基类必须要在主构造函数中立即初始化
// open 标识该类可以被继承 open class Mouse(name: String, price: Int, address: String) private var name: String = name private var price: Int = price private var address: String = address constructor(name: String) : this(name, 0) constructor(name: String, price: Int) : this(name, price, "") fun printInfo() println("name = $name , price = $price, address = $address") class AppleMouse(name: String, price: Int, address: String, wireless: Boolean) : Mouse(name, price, address)
-
如果子类中没有主构造函数,那么必须要在此构造函数中调用 super 关键字初始化父类,或者调用另一个此构造函数。初始化父类时,可以调用父类的不同的构造函数。
重写
重写方法
在继承基类时,子类可以重写基类中的有 open 关键字修饰的方法,如果没有 open 修饰的方法,是允许子类重写的,重写方法时,需要使用 override 关键字标识。
在 IDEA IDE 中,在子类中可以使用快捷键 Ctrl + O 快速重写父类中被 open 关键字修饰的方法
// 基类有默认的无参构造器,所以子类继承时,
open class Phone
open fun getColor()
println("phone has many colors")
class ApplePhone(private var rom: Int) : Phone()
override fun getColor()
super.getColor()
println("iPone color is red")
fun printInfo()
println("the rom is $rom")
fun main(args:Array<String>)
var iPhone = ApplePhone(4)
iPhone.getColor()
输出结果:
iPone color is red
如果一个类继承了多个类或接口,并且父类或接口中有相同的名字的方法需要重写,那么子类这时候必须重写该方法,并且如果子类想区分父类中的方法,可以使用 super 关键字调用不同的父类方法。
/**
* 类中的 open 关键字标识该类可以被继承
*/
open class Fly
/**
* 方法中的 open 关键字标识该方法可以被重写
*/
open fun show()
println("Fly ......")
/**
* interface 不用写 open, 默认就是 open 的
*/
interface Run
/**
* 接口中的方法默认是 open 的
*/
fun show()
println("Run .....")
/**
* 子类实现接口时不用写 (), 因为接口是没有构造函数的
*/
class Bird : Fly(), Run
override fun show()
super<Fly>.show() // 使用 super 关键字来调用不同父类中的同名方法
super<Run>.show()
输出结果:
Fly ......
Run .....
重写变量
- 基类中被重写的变量也要有 open 关键字的声明,
- 子类可以使用 var 类型的变量去重写父类中 val 类型的变量,但是不能使用 val 类型的变量去重写父类中 var 类型的变量(如果使用 val 类型的变量去重写父类的 var 类型的变量,那么子类这个 val 类型的变量就会多一个 set 方法,而 val 类型的变量是不允许有 set 方法的)
open class Car(maxSpeed: Int, price: Int)
open val maxSpeed: Int = maxSpeed
open var price: Int = price
class Maserati(maxSpeed: Int, price: Int) : Car(maxSpeed, price)
/**
* 子类使用 var 类型重写父类中的 val 类型的变量
*/
override var maxSpeed: Int = maxSpeed
/**
* 子类使用 val 类型重写父类中的 var 类型的变量,========>>>>>>编译报错
*/
override val price: Int = price
以上是关于Kotlin 类与继承的主要内容,如果未能解决你的问题,请参考以下文章