Kotlin(八)之泛型

文章目录

入门

  1. 型变(Variant)有三种基本形态

    • 协变(Covariant): string->object (子类到父类的转换)
    • 逆变(Contravariant):object->string (父类到子类的转换)
    • 不变(Invariant):基于上面两种情况,不可变
  2. 对于简单的泛型类、泛型函数与java差不多。Kotlin泛型的是型变支持,kotlin提供了【声明处型变】和【使用处型变】,java只支持【使用处型变】

  3. 代码

    fun main() {
    
        //无法推断,编译错误
        //var parent0 = Parent()
        var parent0 = Parent<String>()
        println(parent0.info) //null
    
        val parent1 = Parent<String>("苹果")
        println(parent1.info)//苹果
    
        //显示指定类型
        var parent2: Parent<Int> = Parent<Int>(3)
        println(parent2.info)//3
    
        //可省略"菱形语法",自动推断处Apple<Double>
        var parent3 = Parent(3.5)
        println(parent3.info)//3.5
    
        val child = Child()
        println(child.info)//child
    
    }
    
    /**
     * 定义泛型类
     */
    open class Parent<T> {
        open var info: T?
    
        constructor() {
            info = null
        }
    
        constructor(info: T) {
            this.info = info
        }
    }
    
    open class Child : Parent<String> {
    
        constructor() {
            info = "child"
        }
    
        //重写构造器
        constructor(info: String) {
            this.info = info
        }
    }
    

型变

  1. java的泛型是不支持型变的,java采用通配符来解决这个问题。即在java中List<String>并不是List<Object> 的子类,因此不能将List<String>赋值给List<Object>

  2. 如果不支持型变,以下的numSetSet<Number>,则addAll传入的参数也必须是Set<Number>,而Set<Integer> 是无法传入的。但是其实是安全的,java中采用通配符的方式来解决这个问题

    1. 如果addAll定义为这样
    public interface Collection<E> extends Iterable<E> {
        boolean addAll(Collection<E> c)
    }    
    Set<Number> numSet = new HashSet<>();
    Set<Integer> intSet = new HashSet<>();
    numSet.addAll(intSet)
    
    2. java中采用通配符(通配符上限)的方式来支持型变 
    public interface Collection<E> extends Iterable<E> {
        boolean addAll(Collection<? extends E> c)
    }      
    
  3. 泛型存在如下规律

    • 通配符上限(泛型协变)意味着从中取出(out)对象是安全的,但传入对象(in)则是不可靠
    • 通配符下限(泛型逆变)意味着向其中传入(in)对象是安全的,但取出对象(out)则不可靠
    • kotlin利用以上两条规律,抛弃了泛型通配符语法,而是利用in、out来让泛型支持型变
  4. 协变

    fun main() {
        var user = User<String>("jannal")
        println(user.info)
        //因为T支持协变,所以 User<String>可以当作 User<Any>用
        var user1: User<Any> = user
        println(user1.info)
    }
    
    class User<out T> {
        val info: T
    
        constructor(info: T) {
            this.info = info
        }
    
        fun info(): T {
            println(this.info)
            return info
        }
    }
    
    
  5. 逆变: T只能出现在方法的形参声明中,不能出现在方法的返回值声明中。一旦声明了逆变,可以安全的将Person<Any>Person<CharSequence>赋值给Person<String>,只要尖括号中的类型是String的父类即可。

    fun main() {
        var person = Person<Any>()
        person.info(111)
        var person2: Person<String> = person
        person2.info("jannal")
    }
    
    class Person<in T> {
        fun info(t: T) {
            println(t)
        }
    }
    
  6. 如果一个类中有的方法使用泛型声明返回类型,有的方法使用泛型声明形参类型,那么该类就不能使用声明处型变。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值