MST

星途 面试题库

面试题:Kotlin协方差与反协方差在函数类型中的应用

在Kotlin中,定义两个函数类型`Function1<in T, out R>`和`Function2<in T1, in T2, out R>`,请举例说明如何正确使用协方差和反协方差来处理这两个函数类型,使得类型安全性得到保证,并且解释在函数类型参数和返回值上使用协方差和反协方差的不同效果及适用场景。
32.9万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试

1. 协方差(Covariance)和反协方差(Contravariance)基础概念

  • 协方差:如果类型 A 是类型 B 的子类型,那么 List<A>List<B> 的子类型。在函数类型中,返回值类型使用协方差,意味着返回值类型可以是更具体的类型。
  • 反协方差:如果类型 A 是类型 B 的子类型,那么 Comparable<B>Comparable<A> 的子类型。在函数类型中,参数类型使用反协方差,意味着参数类型可以是更通用的类型。

2. Function1<in T, out R> 示例

// 定义一个超类和子类
open class Animal
class Dog : Animal()

// 使用协方差和反协方差的函数示例
fun processFunction1(func: Function1<in Animal, out String>) {
    val result = func(Dog())
    println(result)
}

// 定义一个符合要求的函数
fun dogToString(dog: Dog): String = "This is a dog"

fun main() {
    // 这里因为 Function1 的第一个参数是 in,所以可以传入接受 Dog 类型(Animal 的子类)的函数
    // 第二个参数是 out,所以返回值类型是 String
    processFunction1(::dogToString)
}

在这个例子中,Function1<in T, out R> 里,in T 表示参数类型 T 是反协变的,所以可以接受 Animal 类型或者其子类 Dog 类型的参数。out R 表示返回值类型 R 是协变的,所以返回值类型可以是 String 类型或者其父类类型。

3. Function2<in T1, in T2, out R> 示例

// 定义另一个超类和子类
open class Plant
class Flower : Plant()

fun processFunction2(func: Function2<in Animal, in Plant, out String>) {
    val result = func(Dog(), Flower())
    println(result)
}

fun animalAndPlantToString(animal: Dog, plant: Flower): String = "A dog and a flower"

fun main() {
    processFunction2(::animalAndPlantToString)
}

对于 Function2<in T1, in T2, out R>in T1in T2 表示两个参数类型 T1T2 都是反协变的,所以可以接受 Animal 及其子类 Dog 类型的第一个参数,以及 Plant 及其子类 Flower 类型的第二个参数。out R 表示返回值类型 R 是协变的,所以返回值类型可以是 String 类型或者其父类类型。

4. 不同效果及适用场景

  • 函数类型参数上的反协方差
    • 效果:允许传递接受更具体类型参数的函数给期望接受更通用类型参数的函数。这样可以提高函数的灵活性,因为可以用更具体的实现来满足通用的需求。
    • 适用场景:当你有一个函数,它需要一个函数类型的参数,并且这个参数函数的参数类型可以是更具体的类型时,使用反协方差。例如在回调函数场景中,回调函数的参数类型可以是更具体的类型,但调用者只关心更通用的类型。
  • 函数类型返回值上的协方差
    • 效果:允许返回值类型是更具体的类型,而调用者只关心更通用的返回值类型。这样可以保证类型安全性,因为具体类型总是满足通用类型的要求。
    • 适用场景:当你有一个函数,它返回一个函数类型,并且希望返回的函数可以返回更具体类型的值时,使用协方差。例如在工厂函数返回一个创建对象的函数,创建的对象可以是更具体的类型,但调用者只关心通用的对象类型。