作者声明:本文仅作为作者学习期间个人理解所发布的内容,不周误笔之处,希望各位读者能在评论区加以斧正,以此促进共同进步。
泛型的定义:
无论是在类、还是在方法中,<T>泛型占位符的定义都只表示这个类/方法支持泛型,并不是表示这个类/方法的返回值和属性都被定义为泛型。也就是说即是使用<T>修饰,你同样可以使用基本类型作为属性和返回值。
//最简单的泛型类格式
class Creature<T>{}
//泛型类支持泛型属性
class Creature<T>(var gender:T){}
//还可以支持泛型方法
class Creature<T>(var gender:T){
fun <T>speak(){
//方法体
}
//支持泛型参数的方法
fun <T>speak(name:T){
//方法体
}
//返回泛型的方法
fun <T>speak(name:T):T{
//方法体
}
//多泛型,此处只做入门讲解,多泛型不做深入
fun <R,T>speak(name:T,age:R):R{
//方法体
}
}
泛型的实现示例:
class Man<T>(var name:T){ fun <T>speak(pre :T){ println("$pre $name") } } fun main() { /*<String>表示这个泛型类/方法具体实现时使用的类型, 由于该类/方法支持泛型, 如果你将泛型类限定为String,那么传递的参数和属性也应该使用String类型 同样你可以使用任何你想用的类型。 此处的<String>可以省略,因为kotlin有自动类型推断 当你传递对应类型参数之后,该类/方法的类型就会被默认为你传递的参数类型 */ var man=Man<String>("张三") //var man=Man("张三") man.speak<String>("我是") }
泛型关键字in、out的作用
在kotlin中in代表代表逆变,对应java中的? super class;
out代表协变,对应java中的? extends class。
为什么要使用这两个关键字?当我们定义泛型类或泛型方法时class P<T>{...}
如果我们想对这个泛型做具体实现,比如创建一个对象var p=p<Any>()
那么再遇到需要该类型的方法时fun consume(p:P<Any>()),他可以作为参数传递;
但是作为Any的子类String,使用string实现泛型var p1=p<String>()
p1却不能直接作为consume方法的参数,需要先类型转换为p1=P<Any>()
为了保证这种转换过程中的安全,所以kotlin使用in、out来保证兼容性
in关键字的使用和实现:
只需要记住:
in修饰的泛型,在做具体实现时,可以使用期望类的父类来实现,并且泛型参数只能用在输入位置,即属性、方法参数
out修饰的泛型在做具体实现时,可以使用期望类的子类来实现,并且泛型参数只能用在输出位置,即方法的返回值
//定义三个类用于验证转换关系,继承关系Creature->People->Man open class Creature (var name:String){ } open class People(name: String) : Creature(name) { } class Man(name: String) :People(name){ } //接收泛型的消费者类 class Consumer<in T>{ //使用in表示期望子类实现时,可以用其父类实现替代 fun consume(item:T){ if (item is Creature){ println(item.name) } } } //定义一个消费方法,接受People中间类作为泛型实现 fun process(con:Consumer<People>){ con.consume(People("张三")) } fun main() { //期望子类,使用父类实现 var creature=Consumer<Creature>() process(creature) }
out关键字的引用和实现
//沿用上面三个继承类 class OnlyRead<out T>{ //out修饰的泛型需要只能作为输出参数,为了输出一个T泛型参数定义一个可空item val item:T?=null fun speak():T?{ println("only read speak") return item } } fun entry(on:OnlyRead<People>){ on.speak() } fun main() { var man=OnlyRead<Man>() entry(man) }