Kotlin

kotlin<第二篇>:类与继承

2022-08-09  本文已影响0人  NoBugException
一、类的定义
class Test {
}
二、构造函数
主构造函数:

class Test(param1 : String, param2 : Int) {

    init {
        println("param1:$param1")
        println("param2:$param2")
    }

}

还可以添加constructor关键字

class Test constructor(param1 : String, param2 : Int) {

    init {
        println("param1:$param1")
        println("param2:$param2")
    }

}

次构造函数:

class Test {

    constructor() { }

    constructor(param1 : String, param2 : Int) { }

}

如果存在主构造函数,必须继承主构造函数:

class Test (param1 : String, param2 : Int) {

    constructor(param1 : String, param2 : Int, param3 : Int) :this(param1, param2) {

    }

}

初始化块(init)和次构造函数的执行顺序:

我们需要知道,init代码块是主构造函数初始化的地方,当主构造函数被实例化的时候,init代码块必然执行。
当次构造函数被实例化的时候,init代码块首先被执行,然后才会执行次构造函数的实现。

私有构造函数:

    class Test private constructor(param1 : String, param2 : Int) {

    private constructor(param1 : String, param2 : Int, param3 : Int) :this(param1, param2) {
      
    }
}

构造函数时可以被私有的,如果被私有,外部将不能访问,也就是说,在外部,不能对该构造方法进行实例化。

private 关键字放在 constructor 前面,默认情况下是 public,public情况下,主构造函数的 constructor 关键字可以被省略。
三、继承
Kotlin和Java不同,Kotlin任何一个非抽象类默认不可以继承,添加 open 关键字可以让非抽象类继承。

open class Person { 
 ... 
} 

例如:

class Student(param1: String, param2: Int) : Person(param1, param2) {
}

open class Person constructor(param1: String, param2: Int){
}

kotlin中函数的重载(覆盖):

open class Person constructor(param1: String, param2: Int){

    fun doSomething(){
        println("Person doSomething")
    }
}

class Student(param1: String, param2: Int) : Person(param1, param2) {
}

val person: Person = Student("zhangsan", 13)
person.doSomething()

打印结果是:
Person doSomething

在子类Student中可以重载父类的方法:

class Student(param1: String, param2: Int) : Person(param1, param2) {

    override fun doSomething(){
        println("Student doSomething")
    }

}

在kotlin中,子类默认是不允许重载父类的方法,如果想要重载,父类的方法必须使用open关键字修饰:

open class Person constructor(param1: String, param2: Int){

    open fun doSomething(){
        println("Person doSomething")
    }

}

如此,打印结果是:
Student doSomething

变量的覆盖:

在父类中,存在一个成员变量 name
open class Person constructor(param1: String, param2: Int){

    val name: String = "zhangsan"

}

在子类中可以访问父类的成员变量:

class Student(param1: String, param2: Int) : Person(param1, param2) {

    fun doSomething(){
        println("my name is $name")
    }

}

如果子类中定义一个和父类成员变量一模一样的成员变量,那么父类的成员变量就被子类的成员变量覆盖了:

open class Person constructor(param1: String, param2: Int){

    open val name: String = "zhangsan"

}

class Student(param1: String, param2: Int) : Person(param1, param2) {

    override val name: String = "lisi"

    fun doSomething(){
        println("my name is $name")
    }

}

父类的成员变量使用 open 修饰, 子类的成员变量使用 override 修饰。


派生类初始化顺序:总是首先初始化基类,然后才会初始化派生类

抽象类:

abstract class Person {

}

使用 abstract 修饰表示一个抽象类,抽象类不能被实例,只能被继承,而 open 关键字修饰的类既可以继承,也可以被实例化。
四、get 和 set 函数
在Java中,定义一个bean是常见的,在bean里面存在 set 和 get 函数,
kotlin中 set 和 get 的语法和ava存在很大的区别:

class Student {
    var name: String
        get() = name
        set(value) {
            this.name = value
        }
}

在 kotlin 中, set 和 get 函数可以写在成员变量后面。
如上代码,set 和 get 没有其它特别处理,可以将 set 和 get 省略。
五、延迟初始化
private lateinit var textView: TextView

当你对一个全局变量使用了lateinit关键字时,请一定要确保它在被任何地方调用之前已经完成了初始化工作,
否则Kotlin 将无法保证程序的安全性。

判断是否已经初始化
if (::textView.isInitialized)
六、接口
接口的定义和声明:

interface MyInterface {
    fun doSomething()
}

接口的实现:

class Student : MyInterface{

    override fun doSomething() {
        TODO("Not yet implemented")
    }

}

kotlin 和 java一样,都支持多个接口实现。
六、可见性修饰符
public、private、protected和internal

默认修饰符是 public

internal:只对同一模块下可见
七、数据类
使用 data 关键字,声明该类为数据类

data class Cellphone(val brand: String, val price: Double) 

Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等
且无实际逻辑意义的方法自动生成,从而大大减少了开发的工作量。
八、密封类
// sealed:密封类,同时具有枚举和普通类的特性
// 密封类及其子类必须声明在同一个 kotlin 文件中
sealed class Result // 反编译得到抽象类
class Success(val code: Int) : Result()
class Exception(val code: Int, val message: String) : Result()

// 在密封类Result中,只定义了两个子类,所以在使用when语句时,不需要else
fun handleResult(result: Result): String{
    return when(result) {
        is Success -> {
            "success"
        }
        is Exception -> {
            "exception"
        }
    }
}
九、内部类和嵌套类
Kotlin 内部类与嵌套类的区别是:
1、内部类会带有一个外部类的对象的引用,嵌套类则没有
2、内部类需要使用 inner class 定义,而嵌套类则使用 class 定义

Kotlin 内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。

Kotlin 内部类使用 this@[外部类名] 持有外部类对象的引用。
十、委托类
类委托:于将一个类的具体实现委托给另一个类去完成

interface Base {
    fun fixbug()
}

class ZhangsanDo : Base {
    override fun fixbug() {
        println("zhangsan fixed a bug")
    }
}

class Lisi : Base {
    override fun fixbug() {
        println("lisi fixed a bug")
    }
}

假设有一个bug, 张三可以解决bug,李四也可以解决bug,如果领导将bug交给张三解决:

val base1 : Base = ZhangsanDo()
base1.fixbug()

如果领导将bug交给李四给解决:

val base2 : Base = Lisi()
base2.fixbug()

但是,存在一种情况,就是李四无法解决这个bug,所以他想委托为张三去解决:

interface Base {
    fun fixbug()
}

class ZhangsanDo : Base {
    override fun fixbug() {
        println("zhangsan fixed a bug")
    }
}

class Lisi : Base by ZhangsanDo() // 委托给张三

如果领导将bug交给张三,张三直接解决:

val base1 : Base = ZhangsanDo()
base1.fixbug()

如果领导将bug交给李四,李四将bug委托给张三,本质上,bug是由张三解决的:

val base2 : Base = Lisi()
base2.fixbug()
十一、委托属性
class Example {
    var p: String by Delegate() // 将p属性的具体实现委托给了Delegate类去完成
}
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

当 p 使用时就会自动执行 Delegate 的 getValue 方法,当 p 被赋值时,就会自动执行 Delegate 的 setValue方法。

val e = Example()
println(e.p)

打印结果是:
Example@234bef66, thank you for delegating 'p' to me!

val e = Example()
e.p = "NEW"

打印结果是:
NEW has been assigned to 'p' in Example@234bef66.

我们可以发现,当对象第一次被调用时:执行getValue函数,当对象第一次赋值时执行setValue方法。


下面使用委托属性,实现懒加载:

class Later<T>(val block : () -> T) {

    var value : Any? = null

    /**
     * 对象第一次被调用时执行
     */
    operator fun getValue(any: Any?, property: KProperty<*>): T {
        println("getValue")
        if (value == null) {
            value = block()
        }
        return value as T
    }

    /**
     * 被复制或对象属性发生变化时执行
     */
    operator fun setValue(any1: Any?, property: KProperty<*>, any2: Any) {
        println("setValue")
    }

}

var p by Later<String> {
    println("p 执行了")
    "A"
}
p.toString()

kotlin已经为我们实现了懒加载机制:by lazy {}

private val uriMatcher by lazy {
    val matcher = UriMatcher(UriMatcher.NO_MATCH)
    matcher.addURI(authority, "book", bookDir)
    matcher.addURI(authority, "book/#", bookItem)
    matcher.addURI(authority, "category", categoryDir)
    matcher.addURI(authority, "category/#", categoryItem)
    matcher
}

by lazy 代码块是Kotlin 提供的一种懒加载技术,代码块中的代码一开始并不会执行,
只有当uriMatcher变量首次被调用的时候才会执行,
并且会将代码块中最后一行代码的返回值赋给uriMatcher。

默认情况下,对于 lazy 属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。
如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将 LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。 
而如果你确定初始化将总是发生在与属性使用位于相同的线程, 那么可以使用 LazyThreadSafetyMode.NONE 模式:它不会有任何线程安全的保证以及相关的开销。

可观察属性:
Delegates.observale()是实现可观察属性赋值后监听的方法;
相反的是有一个方法叫做Delegates.vetoable()是赋值前判断,满足条件才赋值。

class User {
    var name: String by Delegates.observable("<no name>") {
            property, old, new ->
        println("$old -> $new")
    }

    var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
        //如果newValue大于30将不会赋值
        newValue < 30
    }
}

val user = User()
user.name = "first"
user.name = "second"
user.age = 20
println("age:" + user.age)
user.age = 40
println("age:" + user.age)

打印结果:
<no name> -> first
first -> second
age:20
age:20


将属性储存在映射中:

class User(val map: Map<String, Any?>) {
    val name: String by map // 变量name必须和属性名称相同
    val age: Int     by map // 变量age必须和属性名称相同
}

val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))

println(user.name)
println(user.age)

打印结果:
John Doe
25

[本章完...]

上一篇 下一篇

猜你喜欢

热点阅读