Kotlin系列四-----泛型

本文介绍了Kotlin中的泛型,重点讲解了协变的概念,包括声明处协变、类型协变和星投影的使用。此外,还提到了泛型函数和泛型约束,并简要讨论了类型擦除对泛型的影响。通过学习,读者将更深入理解Kotlin泛型的灵活性和安全性。
摘要由CSDN通过智能技术生成

概述:在java中泛型的方便和灵活之处,相信广大程序员深有体会,泛型的使用为代码的封装提供了无限可能,好的东西自然要保存下去,在Kotlin中同样提供了泛型的使用,而且扩展了其功能,并简化了使用方式,本篇文章就从自己学习的角度,对java和kotlin中的泛型进行简单的总结,以便于更好的理解泛型的使用,下面开始学习吧,与 Java 类似,Kotlin 中的类也可以有类型参数,这可能是泛型最基本的使用了吧:

class Box<T>(t: T) {
    var value = t
}
创建一个类Box声明其接受T类型的参数,此时T为泛型,在使用时再具体指定传入参数的类型,现在来创建对象:

val box: Box<Int> = Box<Int>(1)
这样的创建方式,大家都见过这里不再过多叙述,有一点在前面创建对象时提到过的,就是在kotlin中,若程序能能推断出参数的类型,允许省略参数类型:

val box = Box(1)
上面的简单例子让大家了解下kotlin中泛型的基本使用,下面我们通过与java中的对比进行更细致的学习。

一、协变

Java 中的泛型是不型变的,举个例子:String 是Object的子类,但 List<String> 并不是List<Object> 的子类型,我们进一步的尝试java中哪些情况下的是型变的
//创建object的集合
ArrayList< Object> objects = new ArrayList<>();
objects.add(str);
//那创建List<String> 
ArrayList< String> strings = new ArrayList<>();
objects=strings;
分别创建了object 和 strings 的集合,并把string的类型赋值给object此时会报错,这意味着 List<String> 并不是List<Object> 的子类型,把我们调用addAll()把string加入到object中,编译通过,查看addAll的源码:

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}
这里参数并不是传入E而是传入? extends  E,通配符类型参数 ? extends E 表示此方法接受 E 或者 E 的 一些子类型对象的集合,而不只是 E 自身。 这意味着我们可以安全地从其中(该集合中的元素是 E 的子类的实例)读取 E,但不能写入, 因为我们不知道什么对象符合那个未知的 E 的子类型。 反过来,该限制可以让Collection<String>表示为Collection<? extends Object>的子类型。 简而言之,带 extends 限定(上界)的通配符类型使得类型是协变的(covariant)。

现在我们修改上面的object集合:

ArrayList<? extends Object> objects = new ArrayList<>();
objects=strings; 编译通过 OK
此时在向其添加集合就会编译错误,即只允许读取其中的object属性,不允许写入数据:

//此时以下写入的代码报错:
objects.addAll(integers);
objects.addAll(strings);
objects.add(str);
//表示 无法添加 集合,
可以读取 Object o  = objects.get(0);

因为从中可以读取到object的对象,但不知道究竟是object的那个子类故无法添加对象,以上是利用java中已经存在的集合,对型变的通配符的简单介绍,下面我们看看泛型在创建类的时候的使用:
public class TypeClass<T > {
}
接下来我们同样创建对象:
TypeClass<String> stringTypeClass = new TypeClass<>();
TypeClass<Object> objectTypeClass = stringTypeClass;//报错

TypeClass<String> stringTypeClass = new TypeClass<>();
TypeClass<? extends String> objectTypeClass = stringTypeClass;//通过
Kotlin 中的协变:
声明处协变:
   为了修正这一点,我们必须声明对象的类型为 <? extends Object>,这是毫无意义的,因为我们可以像以前一样在该对象上调用所有相同的方法,所以更复杂的类型并没有带来价值。但编译器并不知道。在 Kotlin 中,有一种方法向编译器解释这种情况。这称为声明处型变:我们可以标注 类的类型参数 T 来确保它仅从 <T> 成员中返回(生产),并从不被消费。 为此,我们提供 out 修饰符:
class TypeClass<out T> {
}

var string = TypeClass<String>()
var any : TypeClass<Any> = string
标记为out 的泛型T,在创建的对象为Any时,可以接受其子类的类型。 另外除了  out ,Kotlin 又补充了一个型变注释: in 。它使得一个类型参数 逆变 :只可以被消费而不可以 被生产:
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值