Kotlin泛型相关

一、Kotlin泛型相关

1. Kotlin 泛型和Java泛型一样,都是有两种泛型方式,分别是泛型类和泛型方法,都使用泛型语法结构 <T>。

package com.luckyboy.kotlinlean.chapter08

// 1.泛型类
// 2.泛型方法
fun main() {

    val stringClass = MyClass<String>()
    stringClass.setData("hello")
    val strData = stringClass.getData()
    println(strData)
    val myTypeInstance = MyTypeClass()
    val sayHi = myTypeInstance.method("hello")
    val sayNum = myTypeInstance.method(100)
    println(sayHi)
    println(sayNum)
}


// 泛型类语法结构 <T>
class MyClass<T> {

    private var t:T? =null

    fun setData(t:T){
        this.t = t
    }

    fun getData():T?{
        return t
    }
}


class MyTypeClass {

    //泛型方法
    fun <T> method(param:T):T {
        return param
    }
}

2. 泛型的类型限制,和 Java 一样都具有泛型类型的限制,Kotlin 的泛型可以不写,具有自动推断功能。

class MyNumClass {

    //对泛型函数的参数进行了限定,必须是Num的子类
    fun <T:Number> method(t:T):T{
        return t
    }
}

val doubleData = MyNumClass().method<Double>(20.0)
//val charData = MyNumClass().method<Char>('dd') // Char 不是Num的子类
val intData = MyNumClass().method<Int>(10)
val floatData = MyNumClass().method(10.9) //自动类型推断
println("doubleData $doubleData")
println("intData $intData")
println("floatData $floatData")

3. 扩展函数使用泛型 

//给StringBuilder添加了一个扩展函数 build
//类似于给StringBuilder对象使用了apply 函数一样
fun StringBuilder.build(block:StringBuilder.()->Unit):StringBuilder{
    block()
    return this
}


//增加泛型类型 使得任意类型的对象都可以有build扩展方法
fun <T> T.build(block:T.()->Unit):T {
    block()
    return this
}

//创建任意类型
class Person(var age:Int, var name:String)

fun main() {
    val stringBuilder = StringBuilder()
    /*  stringBuilder.build {
        append("hello")
        append("world")
    }*/
    stringBuilder.apply {
        append("hello")
        append("world")
    }
    println(stringBuilder.toString())

    val person = Person(10, "lisi")
    //注意:因为Person的构造方法的参数是var修饰的,所以可以进行设置,否则是不行的    
    person.build {
        name = "wangwu"
        age = 12
    }
    println("name is ${person.name}, age is ${person.age}")
}

4. 泛型实例化 

因为泛型擦除,所以 a is  T ,T::class.java 都不能使用,Kotlin 中提供了泛型实例化的手段,可以通过 inline 和 reified 这两个关键字来确定运行时的类型。reified 是具体化的意思。

//泛型实例化
fun main() {
    val stringType = generateType<String>()
    val integerType = generateType<Int>()
    println("stringType $stringType")
    println("integerType $integerType")
}

//inline reified
//inline函数
//输出范型数据类型
inline fun <reified T> generateType() = T::class.java

输出结果:

stringType class java.lang.String
integerType class java.lang.Integer

在运行阶段可以确定数据的类型,再举一个实际开发中比较常用的例子:

inline fun <reified T> startActivity(context: Context){
    val intent = Intent(this, T::class.java)
    context.startActivity(intent)
}

//注意尖括号内的范型类型
btn_goto_second.setOnClickListener {
    startActivity<SecondActivity>(this)
}

实例化 SecondActivity::class.java 字节码对象。

二、协变与逆变 out 和 in 

1. 在 Java 的泛型编程中经常会出现的如下情况,函数的参数是某种类型的父类类型的泛型类,例如 B 是 A 的子类,但 MyClass<B> 不是 MyClass<A>的子类,但是要求传入 MyClass<A> 类型的实参,可是能提供的实参只能是MyClass<B>类型的,那么这样就不能进行参数传递了。比如如下的 Java 的例子:

public class lesson04 {

    public static void main(String[] args) {
        showInfo(new MyClass<Person>());
        //showInfo(new MyClass<Student>()); // 不能转换
        //showInfo(new MyClass<Teacher>()); // 不能转换
    }

    static void showInfo(MyClass<Person> personClass){
        Person person = new Person(10, "person");
        String data = personClass.createInfo(person);
        System.out.println("data-> "+data);
    }
    

    static class MyClass<T extends Person> {
        String createInfo(T person){
            return "name is "+person.name+" age is "+person.age;
        }
    }

    static class Person {
         int age;
         String name;

        Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    }

    static class Student extends Person {

        Student(int age, String name) {
            super(age, name);
        }
    }

    static class Teacher extends Person {

        Teacher(int age, String name) {
            super(age, name);
        }
    }

}

如下修改,可以接受范型类的子类: 

static <T extends Person> void showInfoNew(MyClass<T> personClass, T person){
    String data = personClass.createInfo(person);
    System.out.println("data-> "+data);
}

static class MyClass<T> {
    <E extends Person> String createInfo(E person){
        return "name is "+person.name+" age is "+person.age;
    }
}

public static void main(String[] args) {
    Person person = new Person(10, "person");
    showInfoNew(new MyClass<>(), person); 
    Teacher teacher = new Teacher(30, "teacher");
    showInfoNew(new MyClass<Teacher>(), teacher);
}

输出结果:

data-> name is person age is 10
data-> name is teacher age is 30

2. in 和 out 位置

在开始学习协变和逆变之前,需要了解一个约定:一个泛型类或者泛型接口中的方法,它的参数列表是接收数据的地方,因此可以称为 in 位置,而它的返回值是输出数据的地方,因此可以称为 out 位置,如下所示:【暂时将 in 位置记为输入位置,将 out 记为输出位置】

interface MyClass<T> {
    fun method(param: T): T // 第一个参数param的范型T是in位置 第二个返回值范型T是out位置
}

3. 协变

如果 B 是 A 的子类,且 MyClass<B> 是 MyClass<A>的子类型,那么泛型类 MyClass<T> 就是在泛型T上的协变的。如何实现 MyClass<B> 是 MyClass<A>的子类型呢?可以在 MyClass 的泛型T的声明前面加上 out,表示泛型T 只能出现在out 位置,不能出现 in 位置,那么可以称 MyClass<T> 在泛型T上协变的。

//协变的概念
//如果 B是A的子类,而且 MyClass<B> 是 MyClass<A> 的子类
//那么泛型类 MyClass<T> 就是可以说在泛型T上是协变的。
//协变有什么作用 能够使得具有父子类关系的类型变量可以在函数参数中进行类型安全的传参
//普通的情况下是有问题,会与类型转换问题
fun main() {
    showInfo(MyClass_<Person_>(Person_(10, "person")))
    showInfo(MyClass_<Student_>(Student_(11, "student")))
    showInfo(MyClass_<Teacher_>(Teacher_(31, "teacher")))
}

open class Person_(val age: Int, val name: String)
class Student_(age: Int, name: String) : Person_(age, name)
class Teacher_(age: Int, name: String) : Person_(age, name)

// out 表示只读,只能在 out 位置上出现泛型 T,不能在 in 位置上出现
class MyClass_<out T : Person_>(val data:T?) { 
    fun get(): T? {
        return data
    }

    fun showInfo() {
        println("name is ${data?.name},age is ${data?.age}")
    }
}

fun showInfo(myClass: MyClass_<Person_>) {
    val person = myClass.get()
    //println("name is ${person?.name},age is ${person?.age}")
    myClass.showInfo()
}

4. 逆变

如果 B 是 A 的子类,而 MyClass<A> 又是 MyClass<B> 的子类型,那么MyClass<T> 就是在泛型T上逆变的。 逆变是在泛型声明前面加上 in, 表示泛型T只能出现在 in 位置,而不能出现在out位置。

//逆变的概念:
//如果B是A的子类,而且 MyClass<A> 又是 MyClass<B>的子类,
//那么泛型类 MyClass<T> 可以说在泛型T是上逆变的。
fun main() {
    val trans = object : Transformer<Fruit_>{
        override fun transform(t: Fruit_): String {
            return "color is ${t.color}"
        }
    }
    handleTransform(trans)
}

//泛型类型是位于 in 位置,这样可以实现泛型的逆变
interface Transformer<in T> {
    fun transform(t:T):String
}

//如果没有使用 in,则Transformer<Fruit_> 并不是 Transformer<Apple_>的子类型,不能将此类型的参数传入
//但是如果使用 in,则Transformer<Fruit_> 变成是 Transformer<Apple_>的子类型,就可以将此类型参数传入
fun handleTransform(trans: Transformer<Apple_>){
    val apple = Apple_("red")
    val result = trans.transform(apple)
    println("result -> $result")
}

open class Fruit_(val color: String)
class Apple_(color: String) : Fruit_(color)
class Orange_(color: String) : Fruit_(color)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值