前言
在上一篇中,讲解了Kotlin对应的对象,接口,抽象类相关的知识点。在这一篇中,将会讲解Kotlin对应的泛型、扩展函数。
话不多说,直接开始!
1. 泛型
1.1 单泛型参数
open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)
class Dog(val weight: Int)
class MagicBox<T>(item: T) {
var available = false
private var subject: T = item
fun fetch(): T? {
return subject.takeIf { available }
}
}
fun main{
val box1: MagicBox<Boy> = MagicBox(Boy("Jack", 20))
val box2: MagicBox<Dog> = MagicBox(Dog(12))
println("box1 $box1")
box1.available = true
box1.fetch()?.run {
println("this is ${this.javaClass.name} your name = $name")
}
println("box2 $box2")
box2.available = true
box2.fetch()?.run {
println("this is ${this.javaClass.name} your weight = $weight")
}
}
运行效果
box1 MagicBox@51016012
this is Boy your name = Jack
box2 MagicBox@29444d75
this is Dog your weight = 12
这个使用方式和java类似,使用方式也挺简单的,从头看到这的小伙伴,这点代码相信能够直接一眼而过,直接下一个!
1.2 多泛型参数
1.2.1 示例一
open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)
class Dog(val weight: Int)
class MagicBox<T>(item: T) {
var available = false
private var subject: T = item
fun <R> fetch(subjectModFunction: (T) -> R): R? {
return subjectModFunction(subject).takeIf { available }
}
}
fun main{
val box: MagicBox<Boy> = MagicBox(Boy("Jack", 20))
box.available = true
val man = box.fetch() {
Man(it.name, it.age.plus(10))
}
man?.let { println("${it.name},${it.age}") }
}
运行效果
Jack,30
现在我们看到,这次使用 了MagicBox 里的fun <R> fetch 方法,在这个方法里拥有两个泛型:一个是T,一个是R,其中T泛型,我们知道是由外部传入而决定的;而R泛型,确实由return subjectModFunction(subject).takeIf { available } 这句代码决定。
如果说available 为true时,那么R类型就为subjectModFunction 方法返回的结果类型;否则就为Null!
而subjectModFunction 返回的类型,由这个方法而决定!所以说在对应方法的闭包里最后表达式是什么类型,那这方法就是什么类型!这和之前讲解的let相似!
T.let(block: (T) -> R): R {}
1.2.2 示例二
open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)
class Dog(val weight: Int)
class MagicBox2<T : Human>(vararg item: T) {
var available = true
var subject = item
fun fetch(index: Int): T? {
return subject[index].takeIf { available }
}
fun <R> fetch(index: Int, subjectModFunction: (T) -> R): R? {
return subjectModFunction(subject[index]).takeIf { available }
}
operator fun get(index: Int): T? = subject[index].takeIf { available }
}
fun main() {
val box1 = MagicBox2(
Boy("Jack", 15)
, Man("Bob", 20),
Human(12)
)
box1.fetch(2)?.run {
println("your age is $age")
}
println("-----------------------")
box1.fetch(0) {
var it = it as Boy
var man= Man(it.name, it.age.plus(3))
"我已经成年了,我年龄为: ${man.age}"
}.run(::println)
println("-----------------------")
box1[1]?.run { println("your age is $age") }
}
运行效果
your age is 12
-----------------------
我已经成年了,我年龄为: 18
-----------------------
your age is 20
其实这个示例重点关注class MagicBox2<T : Human>(vararg item: T) 这里面的vararg 关键字,它的意思有点像java里面的类型Human ... ,意思就是在对应方法里,形参可以传多个进来。里面的subject 自然而然变成了对应的数组类型。
2. 扩展
扩展?什么是扩展?
顾名思义,扩展,就是在对应对象上扩展自己想要的东西,然后可供自己使用。
这么神奇的么?来试试?连Java都没有这玩意!
2.1 属性扩展
fun String.addExt(amount: Int = 1) = this + "!".repeat(amount)
fun <T> T.easyPrint() = println(this)
fun main{
println("abc".addExt(10))
123.easyPrint()
"abc".addExt(10).easyPrint().also { }.let { }
}
运行效果
abc!!!!!!!!!!
123
abc!!!!!!!!!!
是不是很好玩呢?系统核心的对象你都能在之上进行扩展!
既然能这样!那我是不是可以在对应对象里加入非空判断的扩展函数?
2.2 方法扩展
fun String?.printWithDefault(default: String) = println(this ?: default)
fun main{
val nullableString: String? = null
nullableString.printWithDefault("abc")
}
从这个代码上看,扩展函数的意思就是如果说,当前对象为null,那么就返回默认值!
那么问题来了,这样写,如果是Int类型的对象为null该怎么办呢?
于是乎带着问题重新新增了一个扩展!
fun String?.printWithDefault(default: String) = println(this ?: default)
fun Int?.printWithDefault(default: Int) = println(this ?: default)
fun main{
val nullableString: String? = null
nullableString.printWithDefault("abc")
val nullableInt:Int?=null
nullableInt.printWithDefault (12)
}
运行效果
abc
12
看到这运行效果,果然还真是按照这想法来的。但看着看着这样写还是有点java化。
想看看最终效果是咋样的。
infix fun String?.printWithDefault(default: String) = println(this ?: default)
infix fun Int?.printWithDefault(default: Int) = println(this ?: default)
fun main{
val nullableString: String? = null
nullableString printWithDefault "cba"
val nullableInt:Int?=null
nullableInt printWithDefault 13
}
运行效果
cba
13
注意看区别!在原有扩展前加入了infix 关键字,然后在使用的时候,没有使用.方法() 的方式了,而是用空格分隔就能调用对应的方法以及传入对应的参数。
那么现在问题来了!
- 对应的扩展是针对当前类,还是全局类呢?
- 相同对象在不同地方定义同一扩展会怎样?
- 是否跟属性或者方法一样有对应的private/public等修饰符?
3.3 解疑问题
fun <T> Iterable<T>.randomTake(): T = this.shuffled().first()
如图所示
在这里创建了一个新类,里面定义对应的扩展函数。意思就是将集合内里面的元素打乱,随后取第一个元素。进入另一个类,来看看使用体验:
fun main{
val list = listOf("Jason", "Jack", "Tom")
list.randomTake().run(::println)
}
居然发现能够正常使用,运行看下效果哇:
Jack
也就是说,默认的扩展是全局有效。现在验证下第二个问题!
fun <T> Iterable<T>.randomTake(): T = this.shuffled().first()
fun main() {
val list = listOf("Jason", "Jack", "Tom")
list.randomTake().run(::println)
}
当吧扩展复制粘贴到当前类的一瞬间,代码爆红了!很明显这种方式不行!
那阔不阔以通过特殊修饰符(private/public等)来实现相同对象在不同地方定义同一扩展呢?
进入Test.kt
private fun <T> Iterable<T>.randomTake(): T = this.shuffled().first()
发现代码依旧报红!
进入KotlinStudy08.kt
private fun <T> Iterable<T>.randomTake(): T = this.first()
fun main{
val list = listOf("Jason", "Jack", "Tom")
list.randomTake().run(::println)
}
发现代码正常了,也就是说,自定义扩展默认全局,如果想要实现同一扩展在不同地方呈现不同效果的话,可以将对应相同的扩展都标注为private类型。
结束语
好了,本篇到这也结束了!在本篇里相信你对Kotlin对应的泛型以及扩展有所了解!在下一篇里,将会重点讲解Kotlin里面的函数式编程!
|