一、类
1.类的声明
与Java中类似,Kotlin中的类声明使用关键字class:
class Person {
var height: Int=185 //规范写法:冒号后需要紧跟一个空格
}
当你定义的类没有类体时,可以省略花括号:
class Person
2.构造函数
1.在Kotlin中可以有一个主构造函数(一般紧跟在类名后)和多个次构造函数。如下实例为类中只有主构造函数的类:
class Person(height: Int) {
}
2.主构造器不能包含除属性声明的任何代码块,初始化的代码可以放到以init关键字作为前缀的初始代码块中。且多个初始化代码块是按照他们出现的先后顺序执行,若有属性初始化,则初始化代码块和属性初始化一起按照出现的先后顺序一起执行:
fun main(args: Array<String>) {
var person=Person() // kotlin中没有new关键字
}
class Person constructor(){
init {
println(1)
}
var a:String ="string".also(::println)
init {
println(2)
}
}
3.主构造器中的参数可以定义为变量,也可以定义为常量,且可以在初始化代码块和类体重声明的属性初始化器中使用:
class Person(h: Int){
var height: Int=h
}
4.当构造器函数有注解或者有可见性修饰符,那么需要使用constructor关键字修饰:
class Person public @Inject constructor(h: Int){
var height: Int=h
}
5.一个类中可以有多个次构造函数,次构造函数必须使用constructor修饰:
class Person {
constructor(var str: String) {
println(str)
}
}
6.如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可:
class Person(val name: String) {
var children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
3.创建类的实例
Kotlin中没有new关键字,所以要创建类的实例是这样的:
val p=Person()
4.访问权限修饰符
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见
// 文件名:example.kt
package foo
private fun foo() {} // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
internal val baz = 6 // 相同模块内可见
二、继承
在kotlin中有一个顶级父类Any(类似于Java中的Object类)但是Any中只有三个方法:equals()、 hashCode() 与 toString()
默认情况下kotlin中的类都是不能继承的,如果你想要使一个类可以继承,那么在创建类时就应该加上open关键字:
open class Person {
}
如果需要声明一个显式的超类型(继承写法),在类头中把超类型放到冒号之后
fun main(args: Array<String>) {
var person=Person()
val son=Son()
son.p()
}
open class Person constructor(){
fun p(){
println("p")
}
}
class Son:Person(){
}
如果派生类有一个主构造函数,其基类可以(并且必须) 用派生类主构造函数的参数就地初始化。
如果派生类没有主构造函数,那么每个次构造函数必须使用 super 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
1.覆盖方法(方法重写)
与Java类似,Kotlin在进行方法重写时需要使用override关键字对方法进行修饰,且只有父类中使用open修饰的方法才能重写(前提父类也要使用open修饰):
fun main(args: Array<String>) {
var person=Person()
val son=Son()
son.p()
}
open class Person constructor(){
open fun p(){
println("person")
}
}
class Son:Person(){
override fun p(){
println("son")
}
}
注:使用override修饰的方法实际效果也相当在重写的情况下使用open修饰了,如果不想再被其子类重写,那么可以使用final修饰
2.覆盖属性
和方法覆盖一样,属性覆盖也需要满足以下几个条件:
- 父类需要被open修饰
- 父类中属性需要被open修饰
- 子类要显示继承父类
- 子类要进行属性覆盖的属性需要使用override修饰
除此之外,我们可以用一个 var 属性覆盖一个 val 属性,但反之则不行。因为一个 val 属性本质上声明了一个 get 方法, 而将其覆盖为 var 只是在子类中额外声明一个 set 方法。(也就是说可以越来越富有,不能越来越贫穷)
3.派生类初始化顺序
在构造派生类的新实例的过程中,第一步完成其基类的初始化(在之前只有对基类构造函数参数的求值),因此发生在派生类的初始化逻辑运行之前。也就是说,当你实例化一个子类时,首先会初始化父类的主构造方法,父类的初始化代码块和初始化属性,接下来才是子类的。
open class Base(val name: String) {
init { println("Initializing Base") }
open val size: Int =
name.length.also { println("Initializing size in Base: $it") }
}
class Derived(
name: String,
val lastName: String,
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {
init { println("Initializing Derived") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in Derived: $it") }
}
fun main() {
println("Constructing Derived(\"hello\", \"world\")")
val d = Derived("hello", "world")
}
4.调用超类实现(super)
派生类中的代码可以使用 super 关键字调用其超类的函数与属性访问器的实现:
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
override fun draw() {
super.draw()
println("Filling the rectangle")
}
val fillColor: String get() = super.borderColor
}
在一个内部类中访问外部类的超类,可以通过由外部类名限定的 super 关键字来实现:super@Outer:
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}
inner class Filler {
fun fill() { println("Filling") }
fun drawAndFill() {
super@FilledRectangle.draw() // 调用 Rectangle 的 draw() 实现
fill()
println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // 使用 Rectangle 所实现的 borderColor 的 get()
}
}
}
上一篇:Kotlin新手教程二(Kotlin基本数据类型及基础语法)