4.Kotlin的泛型和委托

文章目录

1.泛型的基本用法

泛型,一般我们需要给任何一个变量指定一个具体的类型,泛型允许我们在不指定具体类型的情况下进行编程
两种定义方式:定义泛型类和定义泛型方法

  1. 定义泛型类
class MyClass<T>{
	fun method(param : T):T{
		return param
	}
}
//MyClass是一个泛型类

//调用时
val myClass = MyClass<Int>()
val result = myClass.method(123)
  1. 定义泛型方法
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)
  1. 对泛型类型进行限制
    可以通过指定上界的方式对泛型的类型进行约束
class MyClass {
	fun <T:Number>method(param : T):T{
		return param
	}
}
//只能将method()方法的泛型指定成数字类型
//不指定泛型时,泛型的上界默认是Any?
  1. 应用
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有内联函数,内联函数的代码在编译时会自动被替换到调用它的地方,直接使用实际的类型替代内联函数中的泛型声明,不存在泛型擦除的问题
4.Kotlin的泛型和委托

将泛型实例化:

  1. 该函数必须是内联函数,用inline关键字修饰
  2. 在泛型声明的地方必须加上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")
}

结果:
4.Kotlin的泛型和委托

2.2 泛型实化的应用

  1. 启动一个Activity
val intent = Intent(context,TestActivity::class.java)
context.startActivity(intent)
  1. 新建一个reified.kt文件
inline fun <reified T> startActivity(context:Context){
    val intent = Intent(context,T::class.java)
    context.startActivity(intent)
}
  1. 启动TestActivity
startActivity<TestActivity>(context)
  1. 使用Intent附带一些参数
inline fun <reified T> startActivity(context: Context,block:Intent.() -> Unit){
    val intent = Intent(context,T::class.java)
    intent.block()
    context.startActivity(intent)
}
//增加了一个函数类型参数,并且它的函数类型定义在Intent类当中
  1. 在创建完Intent的实例之后,随即调用该函数类型参数,并把Intent的实例传入,这样调用startActivity()函数时就可以在Lambda表达式中为Intent传递参数了
startActivity<TestActivity>(context){
	putExtra("param1","data")
	putExtra("param2",123)
}

2.3 泛型的协变

  1. 一个泛型类或泛型接口中的方法,它的参数列表是接收数据的的地方,in位置,返回值是输出数据的地方,out位置
    4.Kotlin的泛型和委托
  2. 泛型的协变——假如定义了一个MyClass< T > 的泛型类,A是B的子类型,同时MyClass< A >是MyClass< B >的子类型,就可以称MyClass在T这个泛型上是协变的。
  3. 让 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 泛型的逆变

4.Kotlin的泛型和委托
例子:

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()

}
  1. Set接口所有的方法实现,都是调用了辅助对象中相应的方法实现,这其实就是一种委托模式
  2. 其中少部分方法实现由自己重写,甚至加入一些自己的方法,MySet会成为一个心得数据结构类,委托模式的意义所在
  3. 委托关键字——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()
}
  1. 将p的属性具体实现委托给了Delegate类去完成
  2. 调用p属性时会自动调用Delegate类的getValue()方法
  3. 给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)
上一篇:Java——方法


下一篇:如何在ASP.NET Core 的任意类中注入Configuration