泛型和委托
泛型的基本用法
Java 早在 1.5 版本中就引入了泛型的机制,因此,Kotlin 自然也就支持了泛型功能。但是 Kotlin 中的泛型与 Java 中的泛型有同有异,这里先看与 Java 中相同的部分。
泛型,是指在一般的编程模式下,我们需要给任何一个变量指定一个具体的类型,而泛型允许我们在不指定具体类型的情况下进行编程,这样编写出来的代码将会拥有更好的扩展性。比如 List 就是使用泛型来实现的。
泛型主要有两种定义方式,一种是定义泛型类,另一种是定义泛型方法,使用的语法结构都是<T>
,这个 T 只是一种约定俗成的泛型写法。
fun main(){
// 泛型类的调用方式
val myClass = MyClass<Int>()
val result = myClass.method(123)
println("$result")
// 泛型方法的调用方式
val myClass2 = MyClass2()
// 类型推导机制,所以可省略泛型的指定。
// val result = myClass.method<Int>(123)
val result2 = myClass2.method(123)
println("$result2")
}
/**
* 定义泛型类
*/
class MyClass<T>{
/**
* 方法允许使用 T 类型的参数和返回值
*/
fun method(param: T):T{
return param
}
}
/**
* 定义泛型方法
*/
class MyClass2{
/**
* 泛型方法
*/
fun <T> method(param: T):T{
return param
}
/**
* 指定上界的方式,对泛型的类型进行约束。
* 这里表示,只能将方法的泛型指定为数字类型,比如 Int、Float、Double 等。
*
* 另外,默认情况下,所有泛型都是可以指定成可空类型的,这是因为在不手动指定上界时,泛型的上界默认是 Any?。
* 如果想让泛型的类型不可为空,只需要将泛型的上界手动指定成 Any 就可以了。
*/
fun <T:Number> method2(param: T):T{
return param
}
}
fun main(){
contentResolver.query(uri,null,null,null,null)?.apply {
while (moveToNext()){
...
}
close()
}
contentResolver.query(uri,null,null,null,null)?.build {
while (moveToNext()){
...
}
close()
}
}
/**
* 前面了解过的,此函数的作用与 apply 函数基本一样,区别在于此函数只能作用在 SB 上。
*/
fun StringBuilder.build(block:StringBuilder.() -> Unit):StringBuilder{
block()
return this
}
/**
* 使用泛型,对上面函数进行扩展,让它实现和 apply 函数完全一样的功能。
*/
fun <T> T.build(block: T.() -> Unit):T {
block()
return this
}
类委托和委托属性
委托是一种设计模式,它的基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。Java 对于委托并没有语言层级的实现,而像 C# 语言就对委托进行了原生的支持。Kotlin 中也是支持委托功能的,并且将之分为两种:类委托和委托属性。
类委托,它的核心思想在于将一个类的具体实现委托给另一个类去完成。Set 这种数据结构,它是一个接口,如果要实现它,需要使用它具体的实现类,比如 HashSet。借助委托模式,我们可以轻松实现一个自己的实现类:
/**
* 构造函数中接收了一个 HashSet 参数,这就相当于一个辅助对象。
* 然后在 Set 接口所有的方法实现中,都没有进行自己的实现,
* 而是调动了辅助对象中相应的方法实现,这其实就是一种委托模式。
*
* 委托模式的意义在于:
* 可以只是让大部分的方法实现调用辅助对象中的方法,少部分的方法实现由自己来重写,甚至加入一些自己独有的方法,
* 这样,MySet 就会成为一个全新的数据结构类。
*
* 这种写法的弊端是,如果接口中待实现的方法太多,那么每个都要去这样调用辅助对象中的相应方法实现。
* 这个问题,在 Java 中没有什么解决方案,但在 Kotlin 中可以通过类委托的功能来解决。
*/
//class MySet<T>(val helperSet: HashSet<T>):Set<T> {
//
// override val size: Int
// get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
//
// override fun contains(element: T): Boolean {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//
// override fun containsAll(elements: Collection<T>): Boolean {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//
// override fun isEmpty(): Boolean {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//
// override fun iterator(): Iterator<T> {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//}
/**
* Kotlin 中委托使用的关键字是 by,只需要在接口声明的后面使用 by 关键字,
* 再接上受委托的辅助对象,就可以免去之前所写的一大堆模板式的代码了。
*
* 这两段代码实现的效果是一致的。
*/
//class MySet<T>(val helperSet: HashSet<T>):Set<T> by helperSet{
//
//}
/**
* 如果要对某个方法进行重新实现,只需单独重写。
* 其它 Set 接口中的功能,则和 HashSet 保持一致。
*/
class MySet<T>(val helperSet: HashSet<T>):Set<T> by helperSet{
/**
* 新增方法
*/
fun helloWorld() = println("Hello World")
/**
* 重写方法
*/
override fun isEmpty() = false
}
委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个类去实现。
class MyClass {
/**
* by 关键字连接了左边的 p 属性和右边的 Delegate() 实例。
* 这种写法就代表,将 p 属性的具体实现委托给了 Delegate 类去完成。
* 当调用 p 属性时,会自动调用 Delegate 类的 getValue(),
* 当给 p 属性赋值时,会自动调用 Delegate 类的 setValue()。
*
* 假设这里使用 val 关键字,那么只需要实现 getValue() 即可,
* 因为 p 属性是无法在初始化后被重新赋值的。
*/
var p by Delegate()
}