Kotlin类与继承、属性
一、类
Kotlin 中使⽤关键字 class 声明类。类声明由类名、类头(指定其类型参数、主构造函数等)、类体构成。
类头与类体都是可选的;如果⼀ 个类没有类体,可以省略花括号。
class Empty//省掉了类头、类体
二、构造函数
⼀个类可以有⼀个主构造函数以及⼀个或多个次构造函数。
1.主构造函数
- 主构造函数没有任何注解或者可⻅性修饰符,可以省略这个 constructor 关键字。
- 主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中。
- 初始化块与属性初始化器的执行顺序按照类体中的顺序
- 主构造的参数可以在初始化块和属性初始化器中使用
- 主构造函数中声明的属性可以是可变的(var)或只读的(val)。
class InitOrderDemo(val lastName: String, var age: Int) {
val firstProperty =
"First property: $lastName".also(::println)
init {
println("First initializer block that prints ${lastName}")
}
val secondProperty =
"Second property: ${lastName.length + age++}".also(::println)
init {
println("Second initializer block that prints ${lastName.length + age}")
}
}
2.次构造函数
在类体中用constructor声明。
- 如果类有⼀个主构造函数,每个次构造函数需要委托给主构造函数,使用this,可以直接委托或者通过别的次构造函数间接委托。
- 初始化块会成为主构造函数的⼀部分。委托给主构造函数会作为次构造函数的第⼀条语句,因此所有初始化块与属性初始化器都会在次构造函数体之前执行。
- 即使该类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块
class Person(val name: String) {
var children: MutableList<Person> = mutableListOf()
constructor (name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
class Constructors {
init {
println("Init block")
}
constructor(i: Int) {
println("Constructor")
}
}
另外,⼀个非抽象类没有声明任何(主或次)构造函数,它会有⼀个生成的不带参数的主构造函数。可见性默认public
如果主构造函数的所有的参数都有默认值,编译器会生成 ⼀个额外的无参构造函数
class Customer(val customerName: String = "")
三、创建类的实例
像普通函数⼀样调⽤构造函数,不需要new
1.类成员
- 构造函数与初始化块
- 函数属性
- 嵌套类与内部类
- 对象声明
2.继承
在 Kotlin 中所有类都有⼀个共同的超类 Any(隐式继承),类似java的Object
- 默认情况下,Kotlin 类是最终(final)的,用 open 关键字标记才能被集成
- 类头中把超类型放到冒号之后
- 如果派生类有⼀个主构造函数,其基类必须用派生类主构造函数的参数就地初始化。
open class Base(p: Int)
class Derived(p: Int) : Base(p)
派生类没有主构造函数,那么每个次构造函数必须使用 super 关键字初始化其基类型,或委托给另⼀个构造函数做到这⼀点
class MyView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attr: AttributeSet) : this(context)
}
3.覆盖方法
- 基类和被覆盖方法否需要加上open标记
- 覆盖方法加上override标记
- 禁止再次覆盖,使⽤final关键 字
open class Rectangle() : Shape() {
final override fun draw() { /*……*/ }
}
4.覆盖属性
属性覆盖与方法覆盖类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。
- 每个声明的属性可以由具有初始化器的属性或者具有 get 方法的属性覆盖
- 你也可以用⼀个 var 属性覆盖⼀个 val 属性,但反之则不行
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape
class Polygon : Shape {
override var vertexCount: Int = 0 // 以后可以设置为任何数
}
5.派生类初始化顺序
先完成其基类的初始化,再完成派生类的初始化。
基类构造函数执行时,派生类中声明或覆盖的属性都还没有初始化。如果在基类初始化逻辑中(直接或通过 另⼀个覆盖的 open 成员的实现间接)使用了任何⼀个这种属性,那么都可能导致不正确的行为或运行时故障。设计⼀ 个基类时,应该避免在构造函数、属性初始化器以及 init 块中使用 open 成员
6.调用超类实现
- 用 super 关键字调用其超类的函数与属性访问器的实现
- 在⼀个内部类中访问外部类的超类,可以通过由外部类名限定的 super 关键字来实现:super@外部类的类名,如super@Outer
7.覆盖规则
如果⼀个类从它的直接超类继承相同成员的多个实现,为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的 super,如 super<Base> :
open class Rectangle {
open fun draw() { /* …… */ }
}
interface Polygon {
fun draw() { /* …… */ }// 接⼝成员默认就是“open”的
}
class Square() : Rectangle(), Polygon {
override fun draw() {
super<Rectangle>.draw() // 调用 Rectangle.draw()
super<Polygon>.draw() // 调用 Polygon.draw() }
}
}
8.抽象类
用abstract声明,不再需要open
9.伴生对象
companion关键字标记,该伴生对象的成员可通过只使用类名作为限定符来调用:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create()
四、属性
1.声明属性
属性既可以用关键字 var 声明为可变的,也可以用关键字 val 声明为只读的。
2.Getters 与 Setters
声明⼀个属性的完整语法如下,其初始器(initializer)、getter 和 setter 都是可选的
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
用了幕后字段field,需要使用初始化器。没用幕后字段使用初始化器,编辑器报错:Initializer is not allowed here because this property has no backing field
⾃定义的 getter setter 如下所示:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // 解析字符串并赋值给其他属性
}
var counter = 0
set(value) {
field = if (value >= 0) value else -1
}
1.编辑器常量
可以使用 const 修饰符将其标记为编译期常量。const var 等于public static final 默认是pubic,可以指定
- 位于顶层或者是 object 声明 或 companion object 的⼀个成员
- 以 String 或原生类型值初始化
- 没有⾃定义 getter
2.延迟初始化属性与变量
lateinit 修饰符标记该属性,只能用于在类体中的属性,.isInitialized,判定属性或者变量是否被初始化