读者学习本篇文章前请先学习之前的文章
Kotlin系列已更新:
Kotlin基础学习-入门篇
Kotlin基础学习-第二篇
Kotlin进阶学习-第三篇
Kotlin进阶学习-第四篇
泛型笔者之前写过一篇关于Java泛型的文章
Kotlin的泛型和Java的泛型有同有异,因此本篇文章只学习泛型的基础用法,基础和原理请看上面文章。
泛型类声明如下:
class MyClass{ private var value: T? = null public fun setValue(value: T) { this.value = value } }
使用如下:
var myClass = MyClass() myClass.setValue(123)
声明对象时传入Int,属性value的类型则为Int
泛型方法声明如下:
public funmethod(param: T): T{ return param }
使用如下:
method限定泛型类型(123)
有时候程序员相对泛型的类型进行限制,则需借助继承来实现
声明如下:
funmethod(param: T): T{ return param }
若不指定类型,T会被类型擦除为Any?,?代表可为空,Any则等价于Java的Object
改进第三篇中的build函数在第三篇文章中,我们编写了一个StringBuilder的拓展函数build,其用法与apply一样
fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder {
block()
return this
}
上述写法中build函数只能对StringBuilder生效,若想其他类使用则需下面写法,声明顶层方法:
funT.bulid(block: T.() -> Unit): T { block() return this }
这时任何对象都可使用build函数,效果与apply等价
委托委托是一种设计模式,基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。
Java没有再语法层面对委托模式进行支持,而kotlin是支持的。
委托分为两种类委托和委托属性
类委托类委托则是将一个类的具体实现委托给另一个类去完成
之前我们学习了set,如果我们想实现自己的set,借助委托模式如下:
class MySet(val helpSet: HashSet ) : Set { override val size: Int get() = helpSet.size override fun contains(element: T) = helpSet.contains(element) override fun containsAll(elements: Collection ) = helpSet.containsAll(elements) override fun isEmpty() = helpSet.isEmpty() override fun iterator() = helpSet.iterator() }
MySet全部的实现都是依靠委托类HashSet去完成的,那直接调用HashSet不可以吗?可以,但是委托模式允许我们加入独有的方法,MySet则可成为一个全新的数据结构类,这也是委托模式存在的意义。
上述写法是存在弊端的,若set接口中有非常多的方法,那岂不是MySet都要实现一遍,Java中并没有提供解决方法,但Kotlin可以借助by关键字来解决上述问题
借助by,上述代码可改为:
class MySet属性委托(val helpSet: HashSet ) : Set by helpSet{ //定义自己的新方法 fun helloWorld() = println("Hello world") //重写方法,错误示范,只是举例 override fun isEmpty(): Boolean { return false; } }
类委托是将具体实现委托给另一个类去完成,委托属性则是一个属性的具体实现委托给另一个类去完成
其使用如下:
创建MyClass并声明需要委托的属性
class MyClass {
var p by Delegate()
}
创建Delegate类,并且必须实现get和set方法
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
}
}
上述的get和set,必须使用重载符operator修饰,其中都有两个参数,前一个参数为在哪个类可以使用委托,写成MyClass代表只可在MyClass中使用,第二个参数KProperty<*>是Kotlin的一个属性操作类,用于获取各种属性相关的值,当前用不到,但必须声明,<星>类似于java的>,靠程序员自己去定义,只是上述演示使用星
其原理具体流程如下:当我们给p赋值时,则会调用到Delegate的setValue方法,若获取时则会调用getValue方法。
**注意:**若上述p声明为val,则不需要实现setValue方法
lazylazy时Kotlin提供的一种懒加载技术,使用如下:
val p by lazy {
//初始化操作
}
只有当真正调用p是才会执行Lambda中的代码,对p进行初始化
上述学习了委托,则可对lazy的原理进行分析:
调用lazy函数会创建一个Delegate对象,在构造Delegate时可以将Lambda传给Delegate,因此在后续我们使用p,调用Delegate的getValue方法时就可以先执行Lambda再返回value。
下面仿照lazy实现一个简单的懒加载:
创建Later.kt
class Later(val block: () -> T) { var value: Any? = null operator fun getValue(any: Any?, prop: KProperty<*>): T { if (value == null) { value = block() } return value as T } } //顶层方法,任何地方都可以调用 fun later(block: () -> T) = Later(block)
使用如下:
fun main() {
val p by later {
"abc"
}
println(p + " " + 2)
}
//打印结果 abc 2
上述懒加载只是简单的实现,在开发过程还需使用内置的lazy,其内部有严谨的判空机制。
infix在前几篇文章中学习了map的使用,其有一种初始化map的方法如下:
mapOf("A" to 1, "B" to 2)
之前没有讲解**“A” to 1**这种方式的原理,本节对to的原理进行解析:
to不是关键字,to是一个函数,“A” to 1这种写法是Kotlin提供的语法糖:infix函数,其等价于**A.to(1)**的写法
infix使用下面实现String的startsWith(),最终使用效果类似于**“Hello World” startsWith “Hello”**
使用后infix定义一个beginsWith函数:
infix fun String.beginsWith(prefix: String) = startsWith(prefix)
使用如下:
if ("Hello World" beginsWith "Hello") {
//处理逻辑
}
注意:infix函数只能接受一个参数。
下面再使用一个例子对容器的contains方法进行简化:
infix funCollection .has(element: T) = contains(element)
使用如下:
val list = listOfto和Pair对象原理("a", "b", "c") if (list has "b") { //处理逻辑 }
查看to函数的源码:
public infix fun A.to(that: B): Pair = Pair(this, that)
在上篇文章中我们也对Pair进行了了解,其有两个属性first,second,对应key,value
public data class Pair( public val first: A, public val second: B ) : Serializable { ... }



