这是我参与「第四届青训营 」笔记创作活动的第19天
泛型
基本用法
一般对于静态语言而言,定义变量时需要指定一个具体的类型(或者通过类型推导判断类型),使用泛型可以在不指定具体类型的情况下进行定义,在实际调用时再指定或者推导,这样能提高代码的可扩展性。
定义泛型类
class MyClass<T> {
fun method(param: T): T {
return param
}
}
调用
val myClass = MyClass<Int>()
val result = myClass.method(123)
定义泛型方法
fun <T> method(param: T): T {
return param
}
调用(类型推导)
val myClass = MyClass()
val result = myClass.method(123)
指定上界
写法
class MyClass {
fun <T : Number> method(param: T): T {
return param
}
}
这样 T 就只能是 Number 的子类,比如 Int, Double。
泛型的默认上界是 Any? ,故泛型默认为可空类型。
对 build 函数进行扩展,使其具备 apply 的功能
fun <T> T.build(block: T.() -> Unit): T {
block()
return this
}
委托
委托是一种设计模式,它的基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。
kotlin 将委托功能分为了两种:类委托和委托属性。
委托模式示例
class MySet<T>(val helperSet: HashSet<T>) : Set<T> {
override val size: Int
get() = helperSet.size
override fun contains(element: T) = helperSet.contains(element)
override fun containsAll(elements: Collection<T>) = helperSet.containsAll(elements)
override fun isEmpty() = helperSet.isEmpty()
override fun iterator() = helperSet.iterator()
}
如果只是让大部分的方法实现调用辅助对象中的方法,少部分的方法实现由自己来重写,甚至加入一些自己独有的方法,那么 MySet 就会成为一个全新的数据结构类,这就是委托模式的意义所在。
Kotlin 可以通过 by 关键字来减少委托的模板代码。
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {
// 自己独有的方法
fun helloWorld() = println("Hello World")
// 重写 Set 里 有的方法
override fun isEmpty() = false
}
类委托的核心思想是将一个类的具体实现委托给另一个类去完成,而委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个类去完成。
示例
class MyClass {
var p by Delegate()
}
这种写法就代表着将p属性的具体实现委托给了 Delegate 类去完成。当调用p属性的时候会自动调用 Delegate 类的 getValue() 方法,当给p属性赋值的时候会自动调用 Delegate 类的 setValue() 方法。
当然 Delegate 类需要有具体的实现。
class Delegate {
var propValue: Any? = null
operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
return propValue
}
operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?) {
propValue = value
}
}
这是一种标准的代码实现模板,在 Delegate 类中我们必须实现 getValue() 和 setValue() 这两个方法,并且都要使用 operator 关键字进行声明。
getValue() 方法要接收两个参数: 第一个参数用于声明该 Delegate 类的委托功能可以在什么 类中使用,这里写成 MyClass 表示仅可在 MyClass 类中使用。 第二个参数 KProperty<*> 是 Kotlin 中的一个属性操作类,可用于获取各种属性相关的值,在当前场景下用不着,但是必须在方法参数上进行声明。另外,<*> 这种泛型的写法表示你不知道或者不关心泛型的具体类型,只是为了通过语法编译而已,有点类似于Java中的写法。至于返回值可以声明成任何类型,根据具体的实现逻辑去写就行了,上述代码只是一种示例写法。
setValue() 方法也是相似的,只不过它要接收3个参数。前两个参数和 getValue() 方法是相同的,最后一个参数表示具体要赋值给委托属性的值,这个参数的类型必须和 getValue() 方法返回值的类型保持一致。
另外如果 属性p 是通过 val 关键字声明的,则不用实现 setValue() 方法