为什么 Kotlin 在 sumOf 函数 lambda 中默认不将数字视为“Int”?

Arp*_*kla 3 kotlin

在下面的代码中:

val sum = listOf(1, 2, 3).sumOf { if (it % 2 == 0) 1 else 0 }
Run Code Online (Sandbox Code Playgroud)

Kotlin 给出以下错误:

Kotlin: Overload resolution ambiguity: 
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Int): Int defined in kotlin.collections
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Long): Long defined in kotlin.collections
Run Code Online (Sandbox Code Playgroud)

操场

如果我明确使用toInt(),错误就会消失,但我会收到冗余调用的警告

Kotlin: Overload resolution ambiguity: 
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Int): Int defined in kotlin.collections
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Long): Long defined in kotlin.collections
Run Code Online (Sandbox Code Playgroud)

为什么 Kotlin 不在Int这里自动使用?

Swe*_*per 7

该规范对整数文字的类型进行了以下规定:

\n
\n

不带标记的文字具有特殊的整数文字类型\n取决于文字的值:

\n
    \n
  • 如果该值大于最大值kotlin.Long,则它是非法整数文字,应该是编译时错误;
  • \n
  • 否则,如果该值大于最大值kotlin.Int,则其类型为kotlin.Long
  • \n
  • 否则,它具有一个整数文字类型,其中包含保证能够表示该值的所有内置整数类型。
  • \n
\n
\n

因此,像“1”这样的整数文字没有像kotlin.Intor这样的简单类型kotlin.Long。它有一个“整数文字类型”。

\n
\n

示例:整数文字的0x01值为 1,因此类型为ILT(kotlin.Byte,kotlin.Short,kotlin.Int,kotlin.Long)。整数文字 70000 的值为 70000,无法使用类型表示kotlin.Bytekotlin.Short因此具有 type ILT(kotlin.Int,kotlin.Long)

\n
\n

以下是这些 ILT 的子类型规则。对于你的问题重要的是:

\n
\xe2\x88\x80Ti\xe2\x88\x88{T1,\xe2\x80\xa6,TK}:ILT(T1,\xe2\x80\xa6,TK)<:Ti\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x8b
\n这条规则基本上表明 ILT 的工作方式类似于交集类型。例如,ILT(kotlin.Int,kotlin.Long)是 的子类型kotlin.Int,也是 的子类型kotlin.Long

\n

现在让我们看看你的 lambda { if (it % 2 == 0) 1 else 0 }。它返回文字 0 或文字 1。它们都具有以下类型:

\n
ILT(kotlin.Byte,kotlin.Short,kotlin.Int,kotlin.Long)\n
Run Code Online (Sandbox Code Playgroud)\n

kotlin.Long这是和的子类型kotlin.Int。因此,您的 lambda 可以转换为 a(T) -> Long和 a (T) -> Int,就像 a(T) -> Dog可以转换为 a一样(T) -> Animal一样。

\n

当您使用 时toInt(),只有(T) -> Int重载与返回类型匹配,因为Int不能Long隐式转换为。

\n

显然,如果你toInt()对整个表达式执行此操作,则不会出现多余的toInt警告:

\n
fun main() {\n    val sum = listOf(1, 2, 3).sumOf { (if (it % 2 == 0) 1 else 0).toInt() }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

sumOf另请注意,编译器仅因为用 注释而查看 lambda 返回类型OverloadResolutionByLambdaReturnType。如果不是这样,即使您使用toInt(). 有关详细信息,请参阅使用 lambda 返回类型来优化函数适用性。

\n

它能够在简单情况下选择重载的原因Int如下:

\n
fun foo(x: Int) {}\nfun foo(x: Long) {}\nfun main() { foo(43) }\n
Run Code Online (Sandbox Code Playgroud)\n

是因为重载决策中的“选择最具体的候选者”步骤。在此步骤中,它以不同的方式处理内置数字类型,并考虑Int“最具体”。然而,这一步发生在“使用 lambda 返回类型来优化函数适用性”之前,并且认为(T) -> Int(T) -> Long是同样具体的。

\n