一.泛型类
1.定义
泛型类的构造函数可以接受任何类型。
// 泛型
class MagicBox<T>(item : T) {
var available =false;
private var subject:T =item
}
class Box(val name:String,val age:Int)
class Man(val name:String,val age:Int)
fun main() {
val box1:MagicBox<Box> = MagicBox(Box("小华", 20))
val box2:MagicBox<Man> = MagicBox(Man("小华", 10))
}
MagicBox类指定地泛型参数由放在一对<>里的字母T表示,T是个代表item类型的占位符。MagicBox类接受任何类型的item作为主构造函数值(item :T),并将item值赋给同样是T类型的subject私有属性。
二.泛型函数
1.泛型函数
泛型参数也可以用于函数。
class MagicBox<T>(item : T) {
var available =false;
private var subject:T =item
fun fetch():T?{
return subject.takeIf { available }
}
}
class Box(val name:String,val age:Int)
class Man(val name:String,val age:Int)
fun main() {
val magicBox = MagicBox(Box("小华", 20))
magicBox.available=true
//打印
magicBox.fetch()?.run {
println("you find $name")
}
}
2.多泛型函数
泛型函数或泛型类也可以有多个泛型参数。
class MagicBox<T>(item : T) {
var available =false;
private var subject:T =item
fun fetch():T?{
return subject.takeIf { available }
}
//这里新传进了一个类把通过构造函数传进来的带替了
fun <R> fetch(SubjectModFunction:(T)->R) :R?{
return SubjectModFunction(subject).takeIf { available }
}
}
fun main() {
val magicBox = MagicBox(Box("小华", 20))
magicBox.available=true
//这里传了一个lambda表达式用于传值
val fetch = magicBox.fetch {
Man("小华", 30)
}
fetch?.let {
print("${it.name} ${it.age}")
}
}
三.泛型类型约束
1.泛型类型约束
如果要确保MagicBox里面只能装指定类型的物品,如Human类型,怎么办?
class MagicBox<T: Human>(item : T) {
var available =false;
private var subject:T =item
fun fetch():T?{
return subject.takeIf { available }
}
}
fun main() {
val magicBox:MagicBox<Human> = MagicBox(Box("小华", 20))
}
2.vararg关键字与get函数
MagicBox能存放任何类型的Human实列,但一次只能放一个,如果需要放入多个实例需要用vararg关键字
class ArrayMagicBox<T:Human>(vararg item : T) {
var available =false;
//集合泛型
private var subject: Array<out T> =item
fun fetch(index : Int ): T? {
return subject[index].takeIf { available }
}
//这里和之前哪个一样用法只是加了个下标要从数组取值
fun <R> fetch(index:Int, SubjectModFunction:(T)->R) :R?{
return SubjectModFunction(subject[index]).takeIf { available }
}
}
3.[ ]操作符取值
想要通过[ ]操作符取值,可以重载运算符函数get方法。
class ArrayMagicBox<T:Human>(vararg item : T) {
var available =false;
//集合泛型
private var subject: Array<out T> =item
//通过对象调用get方法获取指定下标值
operator fun get(index: Int):T?=subject[index].takeIf { available }
fun fetch(index : Int ): T? {
return subject[index].takeIf { available }
}
//这里和之前哪个一样用法只是加了个下标要从数组取值
fun <R> fetch(index:Int, SubjectModFunction:(T)->R) :R?{
return SubjectModFunction(subject[index]).takeIf { available }
}
}
4.out
out(协变),如果泛型类只将泛型类型作为函数的返回(输出),那么使用out,可以称之为生产类/接口,因为它主要是用来生产(produce)指定的泛型对象。
interface Production<out T>{
fun product():T
}
5.in
in(逆变),如果泛型类只将泛型类型作为函数的入参(输入),那么使用in,可以称之为消费者/接口,因为它主要是用来消费(consume)指定的泛型对象。
interface Consumer<in T>{
fun consume(item : T)
}
6.为什么使用in&out
父类泛型对象可以赋值给子类泛型对象,用in
子类泛型对象可以复制给父类泛型对象,用out
7.invariant
如果泛型类既将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用out也不用in。
interface ProductionConsumer< T>{
fun consume(item : T)
fun product():T
}
8.reified
有时候,你可能想知道某个泛型参数具体什么类型,reified关键字能帮你检查泛型参数类型。Kotlin不允许对泛型参数T做类型检查,因为泛型参数类型会被类型擦除,也就是说,T的类型信息在运行时是不可知的。
class MagicBoxReified<T:Human> (){
//内联 泛型类型推断
inline fun <reified T> randomOrBackup(backup:()->T):T{
val item= listOf(
Boy("小华",20),
Mna("小刚",24)
)
val last = item.shuffled().last()
return if (last is T){
last
}else{
backup()
}
}
}
open class Human(val age:Int){
}
class Boy(val name:String,age: Int): Human(age){
override fun toString(): String {
return "$name $age"
}
}
class Mna(val name:String,age: Int):Human(age){
override fun toString(): String {
return "\n $name $age"
}
}
fun main() {
val magicBoxReified = MagicBoxReified<Human>()
val randomOrBackup = magicBoxReified.randomOrBackup {
Mna("小红", 30)
}
print(randomOrBackup)
}