泛型,即 "参数化类型",将类型参数化,可以用在类,接口,函数上。
与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
1.1 泛型的优点
类型安全:通用允许仅保留单一类型的对象。泛型不允许存储其他对象。
不需要类型转换:不需要对对象进行类型转换。
编译时间检查:在编译时检查泛型代码,以便在运行时避免任何问题。
1.2 泛型类
TFood类指定的泛型参数由放在一对<>里的字母T表示,T是个代表item类型的占位符。TFood类接受任何类型的item作为主构造函数值(item: T)。
- //1、创建泛型类
- class TFood<T>(item:T) {
- init {
- println(if(item is TApple){(item as? TApple)?.price}else item)
- }
-
-
- }
-
- //2、创建传入的类
- class TApple(var price :Int)
-
-
-
- //3、使用
- fun main() {
- //传入Int类型
- TFood(30)//30
- //传入String类型
- TFood("水果")//水果
- //传入实体对象
- TFood(TApple(13))//13
- }
泛型参数也可以用于函数。
定义一个函数用于获取元素,当且仅当泛型类可用时,才能获取元素。
1.4 泛型接口
- interface IFoodEffect<T>{
- fun effect(item:T)
- }
- //实现接口
- class Banana:IFoodEffect<String>{
- override fun effect(item: String) {
- println(item)//item
- }
-
- }
- //使用
- Banana().effect("常食香蕉有益于大脑,预防神经疲劳,还有润肺止咳、防止便秘")
二、泛型类型约束
指定参数类型:我想让这个泛型类只能传入某种类型。
- open class Vip(price:Int)
-
- class TApple(var price: Int): Vip(price)
- class TFood<T:Vip>(item: T) {
- ...
- }
T:Vip,这样写就表示这里只能传入Vip和其子类。这个就类似Java的 extends T> 上界通配符
out(协变):它只能出现在函数的输出位置,只能作为返回类型,即生产者。
in(逆变):它只能出现在函数的输入位置,作为参数,只能作为消费类型,即消费者。
默认(不变):如果泛型类既将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用out也不用in。
泛型类型即作为输出又作为参数。
- /**
- * 泛型接口 不变
- */
- interface IUnchanged<T> {
- fun originally():T
- fun originally(item:T)
-
- }
- class BigStore:IUnchanged<String>{
- override fun originally(): String {
- return "实现泛型接口 不变---返回类型"
- }
-
- override fun originally(item: String) {
- println("------------不变---传参 ---=="+item)
- }
- }
-
- fun main() {
- println("---------------实现泛型接口------- 不变 默认---------------")
- var iUnchanged=BigStore()
- println(iUnchanged.originally())
- println(iUnchanged.originally("参数"))
- }
它只能出现在函数的输出位置,只能作为返回类型,即生产者。
作用:可以将子类泛型对象可以赋值给父类泛型对象。
- //out
- interface IReturn<out T>{
- fun effect():T
- }
-
- open class Fruit()
- class AppleHn():Fruit()
-
- //生产者
- class FruitMarket:IReturn<Fruit>{
- override fun effect(): Fruit {
- println("FruitMarket effect")
- return Fruit()
- }
- }
- class AppleHnMarket:IReturn<AppleHn>{
- override fun effect(): AppleHn {
- println("AppleHnMarket effect")
- return AppleHn()
- }
- }
-
- fun main() {
- //out:可以将子类泛型对象(AppleHn)可以赋值给父类泛型对象(Fruit)
- var fm:IProduction
= FruitMarket() - println(fm.effect())
- //am的引用类型是Fruit对象
- //但是AppleHnMarket返回的是AppleHn对象。
- //这里将AppleHn对象赋值给Fruit对象并返回。
- var am:IProduction
= AppleHnMarket() - println(am.effect())
- }
它只能出现在函数的输入位置,只能作为参数,即消费者。
作用:可以将父类泛型对象可以赋值给子类泛型对象。
- //in
- interface IConsumer<in T>{
- fun spend(t:T)
- }
-
- class Animal:IConsumer<Fruit>{
- override fun spend(t: Fruit) {
- println("Animal spend Fruit")
- }
- }
-
- class People:IConsumer<AppleHn>{
- override fun spend(t: AppleHn) {
- println("People spend AppleHn")
- }
- }
- fun main() {
- //in:可以将父类泛型对象(Fruit)可以赋值给子类泛型对象(AppleHn)
- var fca: IConsumer
= Animal() - fca.spend(AppleHn())
- println(fca)
- var fcp: IConsumer
= People() - fcp.spend(AppleHn())
- println(fcp)
- }
五、vararg
泛型类一次只能放一个,如果需要放入多个实例呢?
- class BookMany<T : AndroidMany>(vararg item: T) {
- var data: Array<out T> = item
- }
-
- open class AndroidMany(name: String)
- class KotlinMany(var name: String, var price: Int) : AndroidMany(name) {
- override fun toString(): String {
- return "KotlinS(name='$name', price=$price)"
- }
- }
-
- class JavaMany(var name: String, var price: Int) : AndroidMany(name) {
- override fun toString(): String {
- return "JavaS(name='$name', price=$price)"
- }
- }
-
- fun main() {
- var book = BookMany(
- KotlinMany("初学者", 18),
- KotlinMany("进阶者", 28),
- KotlinMany("终结者", 38),
- JavaMany("全面者", 35),
- )
- println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
- }
-
添加完多个对象,如果直接打印book,那么获取到的是个BookMany对象,那么怎么获取book里面的的对象?
重载运算符函数get函数,通过[]操作符取值。
- class BookMany<T : AndroidMany>(vararg item: T) {
- var data: Array<out T> = item
- operator fun get(index:Int) = data[index]
- }
-
- fun main() {
- var book = BookMany(
- KotlinMany("初学者", 18),
- KotlinMany("进阶者", 28),
- KotlinMany("终结者", 38),
- JavaMany("全面者", 35),
- )
- println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
- println(book[0])//KotlinS(name='初学者', price=18)
- println(book[2])//KotlinS(name='终结者', price=38)
- }