如何阅读 kotlin 类型注释

时间:2021-06-09 22:18:39

标签: kotlin

在主要使用动态类型语言工作多年后,我开始使用 kotlin,所以我得到了很多我所看到的,但我仍然因为阅读一些类型注释而绊倒了。

它们中的大多数都有意义(我写了一些 C++ 和打字稿,所以我并不完全熟悉更严格的类型语言)。所以像注释函数的参数和返回类型、变量声明之类的东西是有意义的。

我遇到的问题是更复杂的注释,比如在谈论高阶函数时查看 this explanation of the fold method

fun <T, R> Collection<T>.fold(
    initial: R,
    combine: (acc: R, nextElement: T) -> R
): R {
    var accumulator: R = initial
    for (element: T in this) {
        accumulator = combine(accumulator, element)
    }
    return accumulator
}

我明白了:

  • Collection 是指包含类型为 T 的元素的任意集合
  • fold 方法调用将 R 类型的值命名为 initial 作为第一个参数,将标记为 combine 的可调用函数作为第二个参数
  • 将为集合的每个元素调用可调用函数,其中带有标记为 Racc 类型的累加器和类型为 T 的集合的下一个元素(因为它是一个集合Ts) 标记为 nextElement
  • 可调用函数最后会返回一个类型R
  • fold 方法最后会返回一个类型 R

我可以这样使用它:

val greetings = listOf("hey", "hi", "yo", "what's up")

val personalized = greetings.fold("", { carry, current -> "${carry}\n$current, Chris." })

println(personalized)

这一切都说得通,但是 <T, R>fun 之间的 Collection 是什么意思?那部分叫什么? (当你不知道你要找的东西叫什么时,很难找到解释:P)

更重要的是,文档中是否有一部分专门讨论如何阅读这些注释或每个注释的名称?我一直在浏览文档并在一般情况下搜索有关如何阅读类型注释的解释,但我找不到任何内容。

这感觉像是一个愚蠢的问题,但对于初学者来说,这有点令人生畏,而且文档的编写就好像您已经理解了语言的那部分一样。

2 个答案:

答案 0 :(得分:0)

<T, R> 是类型参数。既然你熟悉C++,就像

template <typename T, typename R>

它恰好位于 Kotlin 中的 fun 关键字之后(以及声明泛型类/接口/类型别名时的类型名称之后),而不是函数定义之前。

答案 1 :(得分:0)

正如 Alexey 已经说过的,fun 关键字后面尖括号之间的这些名称称为“类型参数”。它们用于声明 generic functions

<块引用>

集合是指具有 T 类型元素的任意集合

在这里您可以看到 CollectionT 扮演着不同的角色:Collection 是一个众所周知的定义类型,您引用,而 {{ 1}} 只是您为定义此函数而任意选择的名称。

我们希望编译器检查 T 是否是定义和导入的类型,如果输入错误,则会出现编译错误。 另一方面,我们不希望 CollectionT 如此,因此有必要在特殊的语法位置提及它们,以便编译器知道您只是为它们编造了任意名称为了函数定义。

在类型参数和方法参数之间绘制平行线是很好的。方法参数也是您在签名中定义并在函数体中使用的任意名称,而不是像属性这样的类成员,您无需将它们声明为参数即可访问。

就像调用方法时传递的参数值一样,每次不同的调用都可以不同,类型参数的“值”也是在调用点给出的,每次调用都可以不同(不过,它们经常被推断出来,所以你看不到它们)。

请注意,类型参数的“值”是一种类型(例如 R),而不是通常意义上的值,如字符串“abc”。如果需要,您实际上可以在调用站点上明确指定这些类型:

String

调用站点上的语法类似于声明站点,它使用listOf(1, 2, 3).fold<Int, Int>(42) { acc, e -> acc + e } ,只是它写在函数名之后。 通常,编译器使用调用站点上下文中的参数类型或返回类型很容易推断出这些类型,这就是为什么通常不需要显式指定它们的原因。

在类级别与泛型的区别

接口 <> 中的方法不需要声明这样的类型参数似乎很奇怪,尽管它们使用泛型类型:

List

这是因为 interface MutableList<T> { fun add(element: T): Boolean { //.... } } 在用于方法声明时已经“定义良好”:它已经被定义为 T 接口本身的类型参数。机制相同,区别在于定义的范围:类级别的类型参数由类的实例定义(可以创建ListList<Int>,这是在创建实例时选择),而函数类型参数由每次调用函数定义。

您甚至可以将两者结合起来:

List<String>

此处的 interface List<T> { fun <R> map(transform: (T) -> R): List<R> { //... } } 将由您调用 T 的列表实例决定,但即使在同一列表实例上,每次调用 mapR 也可能不同.

相关问题