面试题答案
一键面试- 定义高阶函数:
- 假设我们有两个类型
A
和B
,B
是A
的子类型(B : A
)。我们要设计一个高阶函数,它接受一个List
,其中元素类型是逆变的,然后返回一个List
,其中元素类型是协变的。 - 首先,定义两个类型:
- 假设我们有两个类型
open class A
class B : A()
- 然后定义高阶函数:
fun <A, B : A> transformList(inList: List<in A>): List<out B> {
return inList.map { it as B }.toList()
}
-
类型投影解释:
- 逆变(
in
):- 在
in A
中,in
关键字用于类型投影,表示A
是逆变的。这意味着函数参数inList
可以接受A
类型或者A
的超类型的列表。例如,如果有一个类SuperA
是A
的超类型,我们可以传递List<SuperA>
给这个函数。这符合逆变的概念,即对于函数参数,子类型的父类型是可接受的。这种特性使得函数更加灵活,能处理不同层次的超类型数据。
- 在
- 协变(
out
):- 在
out B
中,out
关键字用于类型投影,表示B
是协变的。这意味着函数返回的List
中的元素类型是B
或者B
的子类型。因为B
是A
的子类型,协变保证了返回的列表元素类型的安全性。例如,如果有一个类SubB
是B
的子类型,返回List<SubB>
也是符合要求的。协变确保了函数返回的数据类型的一致性和安全性,调用者可以安全地处理返回的列表,知道其中元素至少是B
类型。
- 在
- 逆变(
-
类型投影的重要性:
- 安全性:类型投影通过限制类型的使用范围,保证了类型系统的安全性。逆变确保传入函数的参数类型是安全的,协变确保返回的结果类型是安全的。在上述例子中,
in A
确保了传入的列表元素类型对于函数处理是安全的,out B
确保了返回的列表元素类型对于调用者使用是安全的。 - 灵活性:逆变允许函数接受更广泛的参数类型,提高了函数的复用性。例如,一个接受
List<in A>
的函数可以接受List<SuperA>
,List<A>
等不同类型的列表,只要它们与A
有继承关系。协变则允许返回不同层次的子类型,使得函数的返回值可以根据具体情况更加细化,同时保证类型安全。
总的来说,类型投影在这种场景下是至关重要的,它既保证了类型安全,又增加了函数的灵活性和复用性。
- 安全性:类型投影通过限制类型的使用范围,保证了类型系统的安全性。逆变确保传入函数的参数类型是安全的,协变确保返回的结果类型是安全的。在上述例子中,