* 和 Any 在 Kotlin 中:有什么区别以及如何选择?示例解析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么星号和 Any 容易混淆是正常的

\n\n

如果你使用 Kotlin 至少几周,你肯定遇到过泛型。如果你曾查看过标准库的源代码,或者写过类似 List<*> 的代码,那么你可能会问:“为什么不直接写成 List<Any>?星号似乎意味着‘任何类型’?”

\n\n

这个问题在 Stack Overflow 上非常热门(有数千次浏览)。这并不奇怪:乍一看,* 和 Any 似乎是可互换的。但实际上,它们之间存在巨大差异,如果不注意,你可能会写出无法编译的代码,或者更糟,在运行时静默崩溃。

\n\n

在本文中,我们将一劳永逸地弄清楚 Kotlin 泛型中 *(星投影)和 Any 的区别。你将了解何时使用它们,变体如何工作,以及为什么星号不是“Any”,而是“我不知道里面是什么,但我不在乎”。开始吧!

\n\n

泛型简介:为什么需要它们

\n\n

泛型(参数化类型)使我们能够编写适用于不同类型且不损失类型安全的类和函数。我们不必为 Int、String 和 User 分别编写单独的类,而是编写一个通用的 Box<T> 类。

\n\n

但是,如果我们想编写一个接受任何 Box(无论其内部类型如何)的函数,该怎么办?这时我们的主角——* 和 Any——就登场了。

\n\n

Any 是一个具体类型(几乎等同于 Object)

\n\n

在 Kotlin 中,Any 是所有非空类型的类型层次结构的根。如果你写 List<Any>,这意味着:“一个包含 Any 类型元素的列表”。也就是说,你可以放入 Int、String、User 等任何内容,因为它们都继承自 Any。

\n\n

重要的一点:List<Any> 是一个具体的参数化类型。编译器知道元素类型是 Any。

\n\n
val listOfAny: MutableList<Any> = mutableListOf(1, "hello", 3.14)\nlistOfAny.add(42) // OK\nlistOfAny.add("world") // OK\n
\n\n

这里一切都在预料之中:我们明确声明列表处理 Any。但有一个细微差别:MutableList<Any> 不是 MutableList<String>。你不能将字符串列表传递给期望 Any 列表的地方(除非你使用协变,但这稍后讨论)。

\n\n

星号 (*) 是“我不知道,但我不在乎”

\n\n

星号,即星投影,则完全是另一回事。当你写 List<*> 时,你告诉编译器:“这是一个某种类型的列表,但我不知道具体是什么类型,而且我不在乎”。这是一个未知类型的投影。

\n\n

关键区别:* 不是一个类型,而是一个通配符。它意味着类型未知,编译器会限制你可以对此类对象执行的操作。

\n\n
fun printLength(list: List<*>) {\n    // list.add(1) // 编译错误!无法添加元素\n    println(list.size) // OK:可以获取大小\n    for (item in list) {\n        println(item) // OK:item 的类型是 Any?\n    }\n}\n
\n\n

请注意:当你从 List<*> 读取元素时,它们的类型是 Any?(因为列表中可能包含 null)。但你不能添加元素,因为编译器不知道实际存储的类型。这是为了防止破坏类型安全。

\n\n

主要区别概括

\n\n
    \n
  • List<Any> — 是一个可以包含

博客

书籍推荐