You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求协变与逆变的隐喻解释及Kotlin函数场景原理剖析

用「快递站隐喻」搞懂Kotlin函数的协变、逆变与不变性

先上好记的隐喻

把类的继承和函数的类型替换,比作日常的快递站逻辑:

  • 类的继承:把Animal看成「大号包裹」,Dog是「小号包裹」(小号能塞进大号,反之不行);把Number看成「大号箱子」,Int是「小号箱子」(小号箱子属于大号箱子的范畴)
  • 函数参数:相当于快递站的「收件台」——如果收件台能收大号包裹,那肯定能收小号包裹(因为小号包裹的尺寸在大号的范围内)
  • 函数返回值:相当于快递站的「寄件箱」——如果寄件箱能寄出小号箱子,那肯定能满足需要大号箱子的需求(因为小号箱子本身就是大号箱子的一种)

Kotlin函数的类型规则:Function<in T, out R>

Kotlin里的函数类型(T) -> R本质对应Function<in T, out R>接口,其中:

  • in T:参数类型T是逆变的。简单说:如果A是B的父类,那么Function<A, R>可以替代Function<B, R>使用(对应收件台逻辑:能收大号包裹的台,兼容小号包裹)
  • out R:返回值类型R是协变的。简单说:如果C是D的子类,那么Function<T, C>可以替代Function<T, D>使用(对应寄件箱逻辑:能寄小号箱子的,兼容大号箱子的需求)

结合你的代码拆解

基础代码结构

open class Animal
class Dog : Animal()

fun doSomething(fn: (Dog) -> Number) {}

这里doSomething需要的函数是:能接收Dog(小号包裹),返回Number(大号箱子)的函数,对应Function<Dog, Number>。

为什么传入animalToIntMethod能运行?

看要传入的函数:

val animalToIntMethod: (Animal) -> Int = { TODO() }
doSomething(animalToIntMethod)

我们分参数和返回值两部分验证规则:

  1. 参数部分(逆变验证):
    animalToIntMethod的参数是Animal(大号包裹),而doSomething要求的参数是Dog(小号包裹)。根据逆变规则,能处理所有Animal的函数,必然能处理Dog(毕竟Dog是Animal的子类)——就像能收大号包裹的收件台,绝对能收下小号包裹,完全没问题。
  2. 返回值部分(协变验证):
    animalToIntMethod返回Int(小号箱子),而doSomething要求返回Number(大号箱子)。根据协变规则,返回Int等价于返回了一个Number(因为Int继承自Number)——就像快递站寄了个小号箱子,完全满足对方要大号箱子的需求(小号属于大号的范畴)。

一句话总结

  • 逆变(in):参数用父类替代子类,函数能处理更大范围的输入,自然兼容小范围
  • 协变(out):返回值用子类替代父类,更具体的结果能满足更宽泛的需求
  • 不变性:如果函数类型既没有in也没有out,那必须严格匹配类型,不能替换

内容的提问来源于stack exchange,提问作者Nadar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 23:35:18