1、定义泛型接口、泛型类
泛型在定义类,接口,函数时使用泛型形参,这个泛型形参将在声明变量、创建对象,调用方法时动态地指定(传入实际的类型,也可称为类型实参)
可以为任何类、接口增加泛型声明
//定义App类时使用了泛型声明
open class Apple<T>{
//使用泛型T定义属性
open var info:T?
constructor(){
info=null
}
//使用泛型T来定义构造器
constructor(info:T){
this.info=info
}
}
fun main(args:Array<String>){
//由于传给泛型T的是String,所有构造器的参数只能是String
var a1:Apple<String> = Apple<String>("苹果")
//由于传给泛型T的是Int,所有构造器的参数只能是Int
var a2:Apple<Int> = Apple(3)
//由于传给泛型T的是Double,系统推断泛型形参为Double类型
var a3=Apple(5.67)
println(a1.info)
println(a2.info)
println(a3.info)
}
打印:
苹果
3
5.67
2、从泛型派生子类
当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或者从该父类派生子类。
当使用这些接口,父类时不能再包含泛型形参。
父类形参为泛型形参,子类应当明确泛型形参的具体类型
//定义App类时使用了泛型声明
open class Apple<T>{
//使用泛型T定义属性
open var info:T?
constructor(){
info=null
}
//使用泛型T来定义构造器
constructor(info:T){
this.info=info
}
}
class A1:Apple<String>(){
//正确重写了父类的属性,属性类型与父类Apple<String>的属性类型完全相同
override var info:String?=null
get() = "子类"+super.info
}
fun main(args:Array<String>){
var a1:A1=A1()
println(a1.info)
}
打印
子类null
3、泛型型变的需要(实际就是转型,便于添加一个类型的集合到另外一个类型的集合里)
型变实际就是转型,便于添加一个类型的集合到另外一个类型的集合里。
java是通过通配符?来完成的。但通配符的上限是Object类
泛型存在如下规律
通配符上限(泛型协变)意味着从中取出(out)对象是安全的,但传入对象(in)则不可靠
通配符下限(泛型逆变)意味着想起传入(in)对象是安全的。但取出对象(out)则不可靠
kotlin抛弃了泛型通配符语法,而是利用in,out让泛型支持型变
4、声明处型变
kotlin处理型变的规则
- 如果泛型只需要出现在方法的返回值声明中(不出现在形参声明中),那么该方法就只是取出泛型对象,因此该方法就支持泛型协变(相当于通配符上限);如果一个类的所有方法都支持泛型协变,那么该类的泛型参数就可以使用out修饰
- 如果泛型值需要出现在方法的形参声明中(不出现在返回值声明中),那么该方法就只是传入泛型对象,因此该方法就支持泛型逆变(相当于通配符下限);如果一个类的所有方法都支持泛型逆变,那么该类的泛型参数可以用in修饰
支持协变的案例
class User<out T>{
//此处不能用var,否则就有setter方法,setter方法会导致T出现在方法形参中
val info:T
constructor(info:T){
this.info=info
}
fun test():T{
println("执行test方法")
return info
}
}
fun main(args:Array<String>){
//此时T的类型是String
var user=User<String>("疯狂教育体系")
println(user.info)
//对于u2而言,她的类型是User<Any>,此时T的类型是Any,由于程序声明了T支持协变,因此User<String>可当成User<Any>使用
var u2:User<Any> = user//取出数据,所有将上限传入小类型是可行的
println(u2.info)
}
打印:
疯狂教育体系
疯狂教育体系
支持逆变的案例
class Item<in T>{
fun foo(t:T){
println(t)
}
}
fun main(args:Array<String>){
//此时T的类型是Any
var item=Item<Any>()
item.foo(20)
var im2:Item<String> =item
//im2的实际类型是Item<Any>,因此她的foo的参数只要是Any即可
//但我们声明了im2的类型为Item<String>,因此传入的参数值可能是String,所以程序肯定是安全
im2.foo("疯狂kotlin讲义")
}
打印
20
疯狂kotlin讲义
kotilin处理规则
- 如果泛型T只出现在该类的方法的返回值声明中,那么该泛型参数形参即可使用out修饰T
- 如果泛型T只出现在该类的方法的形参声明中,那么该泛型参数即可使用in修饰T
泛型在参数声明的时候出现,这是声明处型变
5、使用处型变:类型投影
声明处型变导致此类所有方法都要用泛型声明返回值类型
使用处型变就是在使用泛型时对其使用out或in修饰
看不下去了,过吧