Kotlin - `if` 和 `when` 表达式的类型

Kotlin - Type of `if` and `when` Expressions

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

这是 when 表达式,returns 不同类型:

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 的类型?

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

一样

在编译时,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类型的对象。


感谢 的快速解释:

There's a difference between the declared type of a variable and the actual type of the object it references. It's no different than doing val x: Any = "Hello, Wold!";

该变量的类型是 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.
    }
}