【问题标题】:Kotlin - Type of `if` and `when` ExpressionsKotlin - `if` 和 `when` 表达式的类型
【发布时间】:2019-10-21 04:29:11
【问题描述】:

我了解 Kotlin 是一种静态类型语言,所有类型都是在编译时定义的。

这是一个返回不同类型的when 表达式:

fun main(){

    val x = readLine()?.toInt() ?: 0

    val y = when(x){
        1 -> 42
        2 -> "Hello"
        else -> 3.14F
    }

    println(y::class.java)
}

在运行时(JVM 1.8 上的 Kotlin 1.3.41)这是输出:

x = 1 时,它会打印class java.lang.Integer

x = 2 时,它会打印class java.lang.String

否则,它会打印class java.lang.Float

编译器什么时候确定y的类型?或者,编译器如何在编译时推断y的类型?

【问题讨论】:

  • y 很可能被键入为Any,这是所有类型的通用超类型。换句话说,通过显式输入,它看起来像val y: Any = when(x) {...}
  • @Slaw 这就是我的猜测,但想知道Any 如何在println(y::class.java) 语句中转换为IntegerString 等。
  • 不是。变量的声明类型和它引用的对象的实际类型是有区别的。这和val x: Any = "Hello, Wold!"; 没什么不同。

标签: kotlin jvm static-typing


【解决方案1】:

实际上,在这种情况下,when 表达式的类型解析为Any,因此y 变量可以具有任何值。 IDE 甚至会警告您,Conditional branch result of type X is implicitly cast to Any,至少 Android Studio 会发出警告,Kotlin Playground 也会发出警告。

【讨论】:

  • 一般来说,它解析为所有相关类型中最接近的公共超类型。例如,如果分支返回ListSet,则结果类型将为Collection(类似地,其类型参数将是最接近的公共超类型)。 (虽然,奇怪的是,这似乎不适用于数字类型;您希望它推断出NumberIntFloat,但它推断出Any。不知道为什么......)
【解决方案2】:

该变量的类型是 Any(作为所有这些类型的最小超类),但基础值未触及。

这是什么意思?您只能安全地访问所有这些类型共有的属性(因此只有Any 类型可用的属性。而::class.java 属性适用于所有类型。

查看这个示例 - 我使用了一些其他类型来很好地可视化它的含义。

abstract class FooGoo {
    fun foogoo(): String = "foo goo"
}

class Foo: FooGoo() {
    fun foo(): String = "foo foo"
}

class Goo: FooGoo() {
    fun goo(): String = "goo goo"
}

class Moo {
    fun moo(): String = "moo moo"
}

fun main(x: Int) {
    val n = when (x) {
        0 -> Foo()
        1 -> Goo()
        else -> throw IllegalStateException()
    } // n is implicitly cast to FooGoo, as it's the closes superclass of both, Foo and Goo

    // n now has only methods available for FooGoo, so, only `foogoo` can be called (and all methods for any)

    val m = when (x) {
        0 -> Foo()
        1 -> Goo()
        else -> Moo()
    } // m is implicitly cast to Any, as there is no common supertype except Any

    // m now has only methods available for Any() - but properties for that class are not changed
    // so, `m::class.java` will return real type of that method.

    println(m::class.java) // // Real type of m is not erased, we still can access it

    if (m is FooGoo) { 
        m.foogoo() // After explicit cast we are able to use methods for that type.
    }
}

【讨论】:

  • 很好的解释!所以when 表达式返回最小的可能超类,这是有道理的。
【解决方案3】:

在编译期间,y 的推断类型是 Any,它是 Kotlin 中所有类型的超类型。在运行时,y 可以引用 [字面上] 任何类型的对象。 IDE 生成警告"Conditional branch result of type Int/String/Float is implicitly cast to Any"

在示例中,

x = 1 时,它指的是java.lang.Integer 类型的对象。

x = 2 时,它指的是java.lang.String 类型的对象。

否则,它引用java.lang.Float类型的对象。


感谢Slaw 的快速解释:

变量的声明类型和它引用的对象的实际类型之间存在差异。和做val x: Any = "Hello, Wold!"没什么区别;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多