教你如何攻克Kotlin中泛型型变的难点(应用篇)

本文详细探讨了Kotlin中泛型的声明点变型与Java使用点变型的区别,强调了声明点变型的便利性。介绍了如何在Kotlin中使用使用点变型,展示了其在实际开发中的应用场景。讲解了Kotlin泛型中的星投影概念,通过源码实例解释了其作为out协变投影的特性。此外,文章通过一个Boolean扩展的实际案例,阐述了泛型型变在开发中的实际应用,提供了一个简化if-else结构的实用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简述: 这是泛型型变最后一篇文章了,也是泛型介绍的最后一篇文章。顺便再扯点别的,上周去北京参加了JetBrains 2018开发者日,主要是参加Kotlin专场。个人感觉收获还是挺多的,bennyHuo和彦伟老师精彩演讲确实传递很多干货啊,当然还有Hali布道师大佬带来了的Kotlin1.3版本的新特性以及Google中国技术推广负责人钟辉老师带来的Coroutines在Android开发中的应用。所以准备整理如下几篇文章为后续发布:

  • 1、Kotlin中1.3版本新特性都有哪些?
  • 2、Kotlin中的Coroutine(协程)在Android上应用(协程学前班篇)
  • 3、Ktor异步框架初体验(Ktor学前班篇)
  • 4、Kotlin中data class的使用(benny大佬在大会上讲的很清楚了,也很全面。主要讲下个人之前踩过的坑,特别是用于后端开发坑更多)

那么今天这篇文章主要是为了给上篇型变文章两个尾巴以及泛型型变是如何被应用到实际开发中的去。并且我会用上篇博客如何去选择相应型变的方法一步步确定最终我们该使用协变、逆变、还是不变,我会用一个实际例子来说明。这篇文章比较简单主要就以下四点:

  • 1、Kotlin声明点变型与Java中的使用点变型进行对比
  • 2、如何使用Kotlin中的使用点变型
  • 3、Kotlin泛型中的星投影
  • 4、使用泛型型变实现可用于实际开发中的Boolean扩展

一、Kotlin声明点变型与Java中的使用点变型进行对比

1、声明点变型和使用点变型定义区别

首先,解释下什么是声明点变型和使用点变型,声明点变型顾名思义就是在定义声明泛型类的时候指明型变类型(协变、逆变、不变),在Kotlin上表现形式就是在声明泛型类时候在泛型形参前面加in或out修饰。使用点变型就是在每次使用该泛型类的时候都要去明确指出型变关系,如果你对Java中型变熟悉的话,Java就是使用了使用点变型.

2、两者优点对比

声明点变型:

  • 有个明显优点就是只需要在泛型类声明时定义一次型变对应关系就可以了,那么之后不管在任何地方使用它都不用显示指定型变对应关系,而使用点变型就是每处使用的地方都得重复定义一遍特别麻烦(又找到一处Kotlin优于Java的地方)。

使用点变型:

  • 实际上使用点变型也是有使用场景的,可以使用的更加灵活;所以Kotlin并没有完全摒弃这个语法点,下面会专门介绍它的使用场景。
3、使用对比

刚刚说使用点变型特别麻烦,一起来看看到底有多麻烦。这里就是以Java为代表,我们都知道Java中要使用型变,是利用?通配符加(super/extends)来达到目的,例如: Function<? super T, ? extends E>, 其中的? extends E就是对应了协变,而? super T对应的是逆变。这里以Stream API中的flatMap函数源码为例

@FunctionalInterface
public interface Function<T, R> {
   
   //声明处就不用指定型变关系
    ...
}

//可以看到使用点变型非常麻烦,定义一个mapper的Function泛型类参数时,还需要指明后面一大串Function<? super T, ? extends Stream<? extends R>>
  <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

声明点变型到底有多方便,这里就以Kotlin为例,Kotlin使用in, out来实现型变对应规则。这里以Sequences API中的flapMap函数源码为例


public interface Sequence<out T> {
   
   //Sequence定义处声明了out协变
    /**
     * Returns an [Iterator] that returns the values from the sequence.
     *
     * Throws an exception if the sequence is constrained to be iterated once and `iterator` is invoked the second time.
     */
    public operator fun iterator(): Iterator<T>
}

public fun <T, R> Sequence<T>.flatMap(transform: (T) -> Sequence<R>): Sequence<R> {
   
   //可以看到由于Sequence声明了协变,所以flatMap函数Sequence中的泛型实参R就不用再次指明型变类型了
    return FlatteningSequence(this, transform, {
   
    it.iterator() })
}

通过以上源码对比,明显看出Kotlin中的声明点变型要比Java中的使用点变型要简单得多吧。但是呢使用点变型并不是一无是处,它在Kotlin中还是有一定的使用场景的。下面即将揭晓

二、如何使用Kotlin中的使用点变型

实际上使用点变型在Kotlin中还是有一定的使用场景,想象一下这样一个实际场景,尽管某个泛型类是不变的,也就是具有可读可写的操作,可是有时候在某个函数中,我们一般仅仅只用到只读或只写操作,这时候利用使用点变型它能使一个不变型的缩小型变范围蜕化成协变或逆变的。是不是突然懵逼了,用源码来说话,你就明白了,一起来看个源码中的例子。

Kotlin中的MutableCollection<E>是不变的,一起来看了下它的定义

public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
   
   //没有in和out修饰,说明是不变
    override fun iterator(): MutableIterator<E>
    public fun add(element: E): Boolean
    public fun remove(element: E): Boolean
    public fun addAll(elements: Collection<E>): Boolean
    public fun removeAll(elements: Collection<E>): Boolean
    public fun retainAll(element
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊喵先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值