在 Kotlin 中,当你看到 Class<out Activity> 或其他泛型类型中带有 out 关键字时,这就涉及到了 Kotlin 的一个重要特性:型变(Variance)。它解决了泛型在继承关系中子类型和父类型之间的赋值兼容性问题。

什么是型变(Variance)?

简单来说,型变定义了当类型A是类型B的子类型时,Generic<A>Generic<B>之间是否存在子类型关系。

Java 默认的泛型是不变(Invariance)的,这意味着即使StringObject的子类型,List** 也不是**List的子类型。这在 Java 中通常通过通配符? extends? super来解决,也就是你提到的?的用法。

Kotlin 引入了outin这两个关键字,提供了更清晰和安全的**声明处型变(Declaration-site Variance)**来解决这个问题。

Java 中的Class<T>和通配符?

在 Java 中,Class<T>也是一个泛型类。

  • Class(不变性): 如果你有Class<String>对象,你不能直接把它赋值给Class<Object>类型的变量。即使StringObject的子类,Class<String>Class<Object>之间没有继承关系
  • ? extends T** (协变): 这就是 Java 的上限通配符**。它表示你可以接受类型为TT任何子类型的泛型。 例如,Class<? extends Activity>意味着这个Class对象可以代表Activity类,或者任何继承自Activity的类(比如MainActivityLoginActivity)。
  • ? super T** (逆变): 这是 Java 的下限通配符**。它表示你可以接受类型为TT任何父类型的泛型。 例如,Comparator<? super String>意味着你可以用一个比较器来比较String类型或者String的任何父类型(比如Object)。

主要看这段就知道什么意思了。协变是上限通配符,逆变是下限通配符。

Kotlin 中的out关键字 (协变)

Kotlin 的out关键字对应 Java 的? extends,用于声明协变

当你将一个泛型类型参数标记为out时(例如class MyGeneric<out T>),它表示T只能作为“生产者”或“输出”类型被使用。这意味着它只能出现在函数返回类型的位置,不能作为函数参数的类型。

对于你遇到的Class<out Activity>

  • 它表示这个Class对象可以代表Activity类,也可以代表任何继承自Activity的类
  • 它解决了 Java 中Class<String>无法赋值给Class<Object>的问题。有了outClass<out Activity>就能接收Class<MainActivity>(假设MainActivityActivity的子类)。
  • 它的作用和 Java 的Class<? extends Activity>完全一样,但 Kotlin 的out是在声明泛型类时就指定了型变规则,而不是在使用时每次都用通配符。这使得代码更简洁、更安全。
特性JavaKotlin
不变List<T>(默认)List<T>(默认)
协变List<? extends T>List<out T>
逆变List<? super T>List<in T>

更新: 2025-08-07 09:57:53
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/hm2uu900rx6eqnsd


相关笔记