Kotlin(五)深入理解 Kotlin 类与接口,androidndk 开发视频
如果类有一个主构造方法,每个次构造方法都需要委托给主构造方法,可以直接委托或者通过别的次构造方法间接委托。委托到另一个构造方法用 this 关键字即可:
class KotlinClass3(val view: View) {var views: MutableList<View> = mutableListOf()
constructor(view: View, index: Int) : this(view) {views.add(view)}}
因为初始化块中的代码实际上是主构造方法的一部分,所以初始化代码块会在次构造方法之前执行。
继承与覆盖
在 Kotlin 中,所有的类默认都是 final 的,如果你需要允许它可以被继承,那么你需要使用 open 声明:
open class Animal(age: Int) {init {println(age)}}
在 Kotlin 中所有类都有一个共同的超类 Any,Any 有三个方法:equals()、hashCode() 与 toString()
在 Kotlin 中继承用:如需继承一个类,请在类头中把超类放到冒号之后:
//派生类有柱构造方法的情况 class Dog(age: Int) : Animal(age)
如果派生类有一个主构造方法,其基类必须用派生类主构造方法的参数初始化。
如果派生类没有主构造方法,那么每个次构造方法必须使用 super 关键字初始化其基类型。
//派生类无柱构造方法的情况 class Cat : Animal {constructor(age: Int) : super(age)}
覆盖规则
覆盖方法
Kotlin 的类成员默认是隐藏的,也就是无法被覆盖,如果要覆盖我们需要用到显式修饰符(open):
open class Animal(age: Int) {init {println(age)}
open fun eat() {
}}
/**
继承*///派生类有主
构造方法的情况 class Dog(age: Int) : Animal(age) {override fun eat() {
}}
eat() 方法上必须加上 override 修饰符。如果没写,编译器将会报错。 如果方法没有标注 open 如 eat(),那么子类中不允许定义相同签名的方法, 不论加不加 override。
覆盖属性
属性覆盖与方法覆盖类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。
open class Animal(age: Int) {open val foot: Int = 0}
class Dog(age: Int) : Animal(age) {override val foot = 4}
属性
属性的声明
Kotlin 类中的属性既可以用关键字 var 声明为可变的,也可以用关键字 val 声明为只读的。
class Shop {var name: String = "Android"var address: String? = null}
fun copyShop(shop: Shop): Shop {val shop = Shop()shop.name = shop.nameshop.address = shop.address// ……return shop}
Getters 与 Setters
声明一个属性的完整语法是
var <propertyName>[: <PropertyType>] [= <property_initializer>][<getter>][<setter>]
其初始器(initializer)、getter 和 setter 都是可选的。如果属性类型可以从初始器 (或者从其 getter 返回值)中推断出来,也可以省略。
案例 1:
val simple: Int? // 类型 Int、默认 getter、必须在构造方法中初始化
案例 2:
我们可以为属性定义自定义的访问器。如果我们定义了一个自定义的 getter,那么每次访问该属性时都会调用它:
val isClose: Booleanget() = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) > 11
如果我们定义了一个自定义的 setter,那么每次给属性赋值时都会调用它。一个自定义的 setter 如下所示:
var score: Float = 0.0fget() = if (field < 0.2f) 0.2f else field * 1.5fset(value) {println(value)}
延迟初始化属性
通常属性声明为非空类型必须在构造方法中初始化。 然而,这经常不方便。例如:属性可以通过依赖注入来初始化, 或者在单元测试的 setup 方法中初始化。
为处理这种情况,你可以用 lateinit 修饰符标记该属性:
class Test {lateinit var shop: Shopfun setup() {shop = Shop()}}
在初始化前访问一个 lateinit 属性会抛出一个特定异常。
我们可以通过属性的 .isInitialized API 来检测一个 lateinit var 属性是否已经初始化过:
if (::shop.isInitialized)println(shop.address)
抽象类与接口
抽象类
类以及其中的某些成员可以声明为 abstract:
abstract class Printer {abstract fun print()}
class FilePrinter : Printer() {override fun print() {}}
接口
Kotlin 的接口可以既包含抽象方法的声明也包含实现。与抽象类不同的是,接口无法保存状态,它可以有属性但必须声明为抽象或提供访问器实现。
可以将 Kotlin 的接口理解为特殊的抽象类
接口的定义与实现
interface Study {var time: Int// 抽象的 fun discuss()fun earningCourses() {println("Android 架构师")}}
//在主构造方法中覆盖接口的字段 class StudyAS(override var time: Int) : Study {//在类体中覆盖接口的字段// override var time: Int = 0override fun discuss() {
}}
解决覆盖冲突
实现多个接口时,可能会遇到同一方法继承多个实现的问题:
interface A {fun foo() {println("A")}}
interface B {fun foo() {print("B")}}
class D : A, B {override fun foo() {super<A>.foo()super<B>.foo()}}
在上例中我们通过 super<>.来解决覆盖冲突的问题。
数据类
我们经常创建一些只保存数据的类,在 Kotlin 中,我们可以通过 data 来声明一个数据类:
data class Address(val name: String, val number: Int) {var city: String = ""fun print() {println(city)}}
数据类的要求
主构造方法需要至少有一个参数;
主构造方法的所有参数需要标记为 val 或 var;
数据类不能是抽象、开放、密封或者内部的;
数据类与解构声明
为数据类生成的 Component 方法 使它们可在解构声明中使用:
val address = Address("Android", 1000)address.city = "Beijing"val (name, city) = addressprintln("name:city")
对象表达式与对象声明
在 Kotlin 中提供了对象表达式来方面我们在需要对一个类做轻微改动并创建它的对象,而不用为之显式声明新的子类。
对象表达式
要创建一个继承自某个(或某些)类型的匿名类的对象,我们会这么写:
open class Address2(name: String) {open fun print() {
}}
class Shop2 {var address: Address2? = nullfun addAddress(address: Address2) {this.address = address}
}
fun test3() {//如果超类型有一个构造方法,则必须传递适当的构造方法参数给它 Shop2().addAddress(object : Address2("Android") {override fun print() {super.print()}})}
如果我们只需要“一个对象而已”,并不需要特殊超类型,那么我们可以简单地写:
fun foo() {val adHoc = object {var x: Int = 0var y: Int = 0}print(adHoc.x + adHoc.y)}
请注意,匿名对象可以用作只在本地和私有作用域中声明的类型。
如果你使用匿名对象作为公有方法的返回类型或者用作公有属性的类型,那么该方法或属性的实际类型会是匿名对象声明的超类型, 如果没有声明任何超类型,就会是 Any ,在匿名对象中添加的成员将无法访问。
class Shop2 {var address: Address2? = nullfun addAddress(address: Address2) {this.address = address
评论