(二十一)Kotlin简单易学 基础语法-泛型
定义泛型类
泛型类的构造函数可以接受任何类型。
MagicBox类指定的泛型参数由放在一对<>里的字母T表示,T是个代表item类型的占位符。MagicBox类接受任何类型的item作为主构造函数值(item:T),并将item值赋值给同样是T类型的subject私有属性。
class MadBox<T>(item: T) {
private var subject: T = item
}
class Boy(val name:String,val age:Int)
class Dog(val weight:Int)
fun main() {
val madBox = MadBox(Boy("Jack", 10))
val madBox2 = MadBox(Dog( 10))
}
泛型参数通常使用字母T(代表英文type)表示,当然,想用其他字母,甚至是英文单词都是可以的。不过,其他支持泛型的语言都在这个约定俗成的T,所以建议你继续用它,这样写出的代码别人更容易理解。
泛型函数
泛型参数也用于函数
定义一个函数用于获取元素,当且仅当MagicBox可用时,才能获取元素。
class MadBox<T>(item: T) {
var availabel = false
private var subject: T = item
fun fetch(): T? {
return subject.takeIf { availabel }
}
}
class Boy(val name: String, val age: Int)
class Dog(val weight: Int)
fun main() {
val madBox = MadBox(Boy("Jack", 10))
madBox.availabel =true
madBox.fetch()?.run {
println("you find $name")
}
}
多泛型参数
泛型函数或泛型类也可以有多个泛型参数
class MadBox<T>(item: T) {
var availabel = false
private var subject: T = item
fun fetch(): T? {
return subject.takeIf { availabel }
}
// return ->R
//泛型函数或泛型类也可以有多个泛型参数
fun <R> fetch(subjectModFunction:(T)->R): R?{
return subjectModFunction(subject).takeIf { availabel }
}
}
class Boy(val name: String, val age: Int)
class Man(val name: String, val age: Int)
class Dog(val weight: Int)
fun main() {
val madBox = MadBox(Boy("Jack", 10))
val fetch = madBox.fetch {
Man(it.name, it.age.plus(15))
}
}
泛型类型约束
如果要确保MagicBox里面只能装指定类型的物品,如Human类型,怎么办?
//泛型类型约束
class MadBox<T :Human>(item: T) {
var availabel = false
private var subject: T = item
fun fetch(): T? {
return subject.takeIf { availabel }
}
// return ->R
fun <R> fetch(subjectModFunction:(T)->R): R?{
return subjectModFunction(subject).takeIf { availabel }
}
}
open class Human(val age: Int)
class Boy(val name: String, age: Int):Human(age)
class Man(val name: String, age: Int):Human(age)
class Dog(val weight: Int)
fun main() {
val madBox = MadBox(Boy("Jack", 10))
val fetch = madBox.fetch {
Man(it.name, it.age.plus(15))
}
}
vararg关键字与get函数
MagicBox能存放任何类型的Human实例,但一次只能放一个,如果需要放入多个实例呢?
//泛型类型约束
class MadBox<T : Human>(vararg item: T) {
var availabel = false
private var subject: Array<out T> = item
fun fetch(index: Int): T? {
return subject[index].takeIf { availabel }
}
// return ->R
fun <R> fetch(index: Int, subjectModFunction: (T) -> R): R? {
return subjectModFunction(subject[index]).takeIf { availabel }
}
fun a(vararg a: Int) {
}
}
open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)
class Dog(val weight: Int)
fun main() {
val madBox = MadBox(
Boy("Jack", 10),
Boy("2", 10),
Boy("3", 10)
)
madBox.fetch(2) {
Man(it.name, it.age)
}
}
[ ] 函数
//泛型类型约束
class MadBox<T : Human>(vararg item: T) {
var availabel = false
private var subject: Array<out T> = item
fun fetch(index: Int): T? {
return subject[index].takeIf { availabel }
}
// return ->R
fun <R> fetch(index: Int, subjectModFunction: (T) -> R): R? {
return subjectModFunction(subject[index]).takeIf { availabel }
}
//get函数
operator fun get(index: Int): T? = subject[index]?.takeIf { availabel }
}
open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)
class Dog(val weight: Int)
fun main() {
val madBox = MadBox(
Boy("Jack", 10),
Boy("2", 10),
Boy("3", 10)
)
madBox[1]
}
out 函数
out(协变),如果泛型类只将泛型类型作为函数(输出),那么使用out,可以称之为生产类/接口,因为它主要生产(produce)指定的泛型。
interface Production<out T> {
fun product(): T
}
in 函数
in(逆变),如果泛型类只将泛型类型作为函数的入参(输入),那么使用in,可以称为消费者类/接口,因为它主要是用来消费(consume)指定的泛型对象。
interface Consumer<in T> {
fun consume(item: T)
}
invariant 函数
如果泛型类型即将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用out,也不用in。
interface ProductionConsumer<T> {
fun product(): T
fun consume(item: T)
}
何时用out/in
父类泛型对象可以赋值给字类泛型对象,用in
字类泛型对象可以赋值给父类泛型对象,用out
out
interface Production<out T> {
fun product(): T
}
interface Consumer<in T> {
fun consume(item: T)
}
interface ProductionConsumer<T> {
fun product(): T
fun consume(item: T)
}
open class Food
open class FastFood : Food()
class Burger : FastFood()
//生产者
class FoodStore : Production<Food> {
override fun product(): Food {
println("product food")
return Food()
}
}
//快餐
class FastFoodStore : Production<FastFood> {
override fun product(): FastFood {
println("product fast")
return FastFood()
}
}
class BurgerStore : Production<Burger> {
override fun product(): Burger {
println("product Burger")
return Burger()
}
}
fun main() {
val foodStore: Production<Food> = FoodStore()
//字类泛型对象可以赋值给父类泛型对象,用out,在java中是不允许的
val foodStore2: Production<Food> = FastFoodStore()
}
in
interface Consumer<in T> {
fun consume(item: T)
}
open class Food
open class FastFood : Food()
class Burger : FastFood()
//消费者
class Everybody : Consumer<Food> {
override fun consume(item: Food) {
println("Eat food")
}
}
class ModernPeople:Consumer<FastFood>{
override fun consume(item: FastFood) {
println("Eat fastFood")
}
}
class American :Consumer<Burger>{
override fun consume(item: Burger) {
println("Eat burger")
}
}
fun main() {
//父类赋值子类
val consumer1:Consumer<Burger> = Everybody()
val consumer2:Consumer<Burger> = ModernPeople()
val consumer3:Consumer<Burger> = American()
}
reified 函数
有时候,你可能想知道某个泛型参数具体是什么类型,reified关键字能帮你检查泛型参数类型。Kotlin不允许对泛型参数T做类型检查,因为泛型参数类型会被类型擦除,也就是说,T的类型信息在运行时是不可知的,Java也有这样规则。
class MadBox<T : Human>() {
inline fun <reified T> randomOrBackup(backup: () -> T): T {
val items = listOf(
Boy("name", 20),
Man("name", 20)
)
val random = items.shuffled().first()
return if (random is T) {
random
} else {
backup()
}
}
}
open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)
class Dog(val weight: Int)
fun main() {
val box1: MadBox<Man> = MadBox()
box1.randomOrBackup {
Man("name", 20)
}
}