文章目录
1.泛型的基本用法
泛型,一般我们需要给任何一个变量指定一个具体的类型,泛型允许我们在不指定具体类型的情况下进行编程
两种定义方式:定义泛型类和定义泛型方法
- 定义泛型类
class MyClass<T>{
fun method(param : T):T{
return param
}
}
//MyClass是一个泛型类
//调用时
val myClass = MyClass<Int>()
val result = myClass.method(123)
- 定义泛型方法
class MyClass {
fun <T>method(param : T):T{
return param
}
}
//调用
val myClass = MyClass()
val result = myClass.method<Int>(123)
//类型推导机制简化
val myClass = MyClass()
val result = myClass.method(123)
- 对泛型类型进行限制
可以通过指定上界的方式对泛型的类型进行约束
class MyClass {
fun <T:Number>method(param : T):T{
return param
}
}
//只能将method()方法的泛型指定成数字类型
//不指定泛型时,泛型的上界默认是Any?
- 应用
fun StringBuilder.build(block:StringBuilder.() -> Unit):StringBuilder{
block()
return this
}
//只能作用在StringBuilder上
//修改
fun <T> T.build(block: T.() -> Unit): T {
block()
return this
}
2.泛型的高级特性
2.1 泛型实化
2.1.1 Java的泛型擦除机制
JDK 1.5中,Java引入了泛型功能,实际上泛型功能通过类型擦除机制来实现,泛型对于类型的约束只在编译时期存在,运行时仍会按照JDK1.5之前的机制来运行,JVM识别不出来在代码中指定的泛型类型。Kotlin也是一样,所以不能使用a is T 或 T::class.java 这样的语法,T的实际类型在运行时已经被擦除了。
Kotlin有内联函数,内联函数的代码在编译时会自动被替换到调用它的地方,直接使用实际的类型替代内联函数中的泛型声明,不存在泛型擦除的问题
将泛型实例化:
- 该函数必须是内联函数,用inline关键字修饰
- 在泛型声明的地方必须加上reified关键字表示该泛型要进行实例化
inline fun <reified T> getGenericType(){
}
实现获取泛型实际类型的功能
inline fun <reified T> getGenericType() = T::class.java
调用:
fun main(){
val result1 = getGenericType<String>()
val result2 = getGenericType<Int>()
println("result1 is $result1")
println("result2 is $result2")
}
结果:
2.2 泛型实化的应用
- 启动一个Activity
val intent = Intent(context,TestActivity::class.java)
context.startActivity(intent)
- 新建一个reified.kt文件
inline fun <reified T> startActivity(context:Context){
val intent = Intent(context,T::class.java)
context.startActivity(intent)
}
- 启动TestActivity
startActivity<TestActivity>(context)
- 使用Intent附带一些参数
inline fun <reified T> startActivity(context: Context,block:Intent.() -> Unit){
val intent = Intent(context,T::class.java)
intent.block()
context.startActivity(intent)
}
//增加了一个函数类型参数,并且它的函数类型定义在Intent类当中
- 在创建完Intent的实例之后,随即调用该函数类型参数,并把Intent的实例传入,这样调用startActivity()函数时就可以在Lambda表达式中为Intent传递参数了
startActivity<TestActivity>(context){
putExtra("param1","data")
putExtra("param2",123)
}
2.3 泛型的协变
- 一个泛型类或泛型接口中的方法,它的参数列表是接收数据的的地方,in位置,返回值是输出数据的地方,out位置
- 泛型的协变——假如定义了一个MyClass< T > 的泛型类,A是B的子类型,同时MyClass< A >是MyClass< B >的子类型,就可以称MyClass在T这个泛型上是协变的。
- 让 MyClass< A >成为MyClass< B >的子类型:一个泛型类在其泛型类型的数据上是只读时,没有类型转换安全隐患,需要让MyClass< T >类中所有方法都不能接收T类型的参数,T只能出现在out位置。
例子1:
会产生类型转换异常:
open class Person(val name:String,val age:Int)
class Student (name:String,age:Int):Person(name,age)
class Teacher (name:String,age:Int):Person(name,age)
class SimpleData <T>{
private var data: T?=null
fun set(t:T){
data = t;
}
fun get():T?{
return data
}
}
class Simple {
fun main(){
val student = Student("Tom",19)
val data = SimpleData<Student>()
data.set(student)
handleSimpleData(data)//这里会报错
val studentData = data.get()
}
fun handleSimpleData(data:SimpleData<Person>){
val teacher = Teacher("Jack",35)
data.set(teacher)
}
}
修改:
修改SimpleData类的代码:
class SimpleData <out T>(val data: T){ //val类型无法修改
fun get():T?{
return data
}
}
fun main(){
val student = Student("Tom",19)
val data = SimpleData<Student>(student)
handleSimpleData(data)//这里会报错
val studentData = data.get()
}
fun handleSimpleData(data:SimpleData<Person>){
val personData = data.get()
}
例子2:
Kotlin默认给许多内置的API加上了协变声明,包含了各种集合的类与接口
//List简化版的源码
public interface List<out E> : Collection<E> {
//List在泛型E前面加上了out关键字,说明List在泛型E上是协变的
override val size:Int
override fun isEmpty():Boolean
override fun contains(element: @UnsafeVariance E) : Boolean
//contains()方法只是判断,不会修改集合中的内容
//加上@UnsafeVariance注解,编译器就会允许泛型E出现在in位置上了
override fun iterator(): Iterator
public operator fun get(index:Int):E
}
2.4 泛型的逆变
例子:
interface Transformer<T> {
fun transformer(t:T):String
}
fun main(){
//Transformer<Person>的匿名类实现
val trans = object : Transformer<Person>{
override fun transformer(t: Person): String {
return "${t.name} ${t.age}"
}
}
hamdleTransformer(trans)//会报错,
//Transformer<Person>不是Transformer<Student>的子类型
}
//接收一个Transformer<Student>类型的参数
fun hamdleTransformer(trans:Transformer<Student>){
val student = Student("Tom",19)
//创建一个Student对象,并调用参数的transform()方法将Student对象转换成一个字符串
val result = trans.transformer(student)
}
修改:
//Transformer在泛型T上是逆变的
interface Transformer<in T> {
fun transformer(t:T):String
}
逆变功能在Kotlin内置API中的应用——Comparable
interface Comparable<in T>{
operator fun compareTo(other:T):Int
}
Comparable在T这个泛型上是逆变的
3. 类委托和委托属性
委托:一种设计模式,操作对象自己不会去处理某段逻辑,把工作委托给另外一个辅助对象去处理
3.1 委托类
将一个类的具体实现委托给另一个类去完成
例如:Set是一个接口,使用时需要使用他的具体的实现类,如HashSet。借助委托模式可以实现一个自己的实现类、
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()
}
- Set接口所有的方法实现,都是调用了辅助对象中相应的方法实现,这其实就是一种委托模式
- 其中少部分方法实现由自己重写,甚至加入一些自己的方法,MySet会成为一个心得数据结构类,委托模式的意义所在
- 委托关键字——by
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet{
}
两段代码的效果一样,第二段代码要是想重写某个方法,只需要单独重写那一个方法
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet{
fun helloWorld() = println("Hello World")
override fun isEmpty() = false
}
3.2 委托属性
将一个属性的具体实现委托给另一个类去完成
class MyClass{
var p by Delegate()
}
- 将p的属性具体实现委托给了Delegate类去完成
- 调用p属性时会自动调用Delegate类的getValue()方法
- 给p属性赋值时会自动调用Delegate类的setValue()方法
因此对Delegate类进行具体的实现
class Delegate{
var propValue:Any? = null
//当用val声明时,不用在Delegate类中实现setValue()方法
operator fun getValue(myClass: MyClass,prop:KProperty<*>):Any?{
return propValue
}
operator fun setValue(myClass: MyClass,prop:KProperty<*>,value:Any?){
propValue = value
}
}
//KProperty<*>是Kotlin中的一个属性操作类,可用于获取各种属性相关的值
//<*>表示不知道或不关心泛型的具体类型
//value:Any? 表示要赋值给委托属性的值
4.实现一个自己的lazy函数
by lazy 的基本语法结构 :val p by lazy { … }
在lazy函数中会创建并返回一个Delegate对象,调用p属性时,实际调用Delegate对象的getValue()方法,然后getValue()方法中又会调用lazy函数传入的Lambda表达式,这样,表达式中的代码块就得到执行了,并且调用p属性后得到的值就是Lambda表达式中最后一行代码的返回值。
实现一个自己的lazy函数
class Later {
class Later<T>(val block:() -> T){
var value:Any? = null
operator fun getValue(any: Any?,prop:KProperty<*>):T{
//Any?表示我们希望Later的委托功能能在所有类中都可以使用
if(value == null){
value = block()
}
return value as T
}
//懒加载技术不会对属性赋值,这里不用实现setValue()方法
}
}
定义顶层函数(不定义在任何类当中的函数),作用:创建Later类的实例,并将接收的函数类型参数传给Later类的构造函数
fun <T> later(block:() -> T) = Later(block)