MST

星途 面试题库

面试题:Kotlin中类型参数约束与逆变协变基础

在Kotlin中,假设我们有一个函数 `fun printList(list: List<*>)`,请解释为什么这里使用 `List<*>` 而不是 `List<Any>`。另外,简要说明类型参数的上界约束在这种场景下如何使用。
14.5万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试
  1. List<*>List<Any> 的区别
    • List<*>
      • List<*> 表示一个类型未知的列表,它是类型投影的一种形式,被称为星投影。这里的 * 代表任何类型。例如,如果一个列表实际存储的是 Int 类型元素,List<*> 可以安全地引用它。它允许我们以只读方式访问列表元素,因为编译器不知道具体类型,所以写操作会受限,这有助于保证类型安全。例如,val list: List<Int> = listOf(1, 2, 3),可以赋值给 val starList: List<*> = list,但是不能进行像 starList.add(4) 这样的操作(因为不知道具体类型,编译器会报错)。
    • List<Any>
      • List<Any> 明确表示列表可以包含任何类型的对象,它不仅仅是只读的,还允许写入任何类型的对象到列表中。例如,如果有 val anyList: MutableList<Any> = mutableListOf(),可以执行 anyList.add("string")anyList.add(1) 等操作。这可能会导致类型安全问题,因为如果后续代码假设列表中都是特定类型(比如 Int),而实际有其他类型(如 String),就会引发运行时错误。
  2. 类型参数的上界约束在这种场景下的使用
    • 类型参数的上界约束使用 where 关键字或者直接在类型参数声明处指定。例如,如果希望函数 printList 能够处理任何类型的列表,但该类型必须是 Number 的子类,可以这样定义函数:
    fun <T : Number> printList(list: List<T>) {
        for (element in list) {
            println(element)
        }
    }
    
    • 这里 <T : Number> 表示类型参数 T 的上界是 Number,即 T 必须是 Number 或者 Number 的子类。这样函数 printList 就可以处理 List<Int>List<Double> 等,但不能处理 List<String>。这使得函数在保证类型安全的同时,又有一定的通用性,只接受符合特定类型层次结构的列表,避免了处理不相关类型带来的错误。