在 Kotlin 中,“::class.simpleName”是做什么的?

In Kotlin, what does "::class.simpleName" do?

val var1: Any = "Carmelo Anthony"

印象中::class.simpleNamereturns对象的变量类型
当我执行以下操作时:

val var1Type = var1::class.simpleName
print(var1Type)

我得到 String 而不是 Any

但是当我这样做的时候

val var2: String = var1

我得到一个Type mismatch: inferred type is Any but String was expected

  • 在 Kotlin 中,::class 运算符有两种形式:
  • 在您的例子中,var1 的运行时类型为 String,但静态类型为 Any
    • 所以 var1::class returns StringKClass,而不是 Any
  • 但是 Kotlin 的类型系统,像大多数静态类型语言一样,不允许隐式缩小转换(即给定一个类型为 String 的变量 var2,你不能 assign-to var2 来自另一个变量 (var3) statically-typed 为 Any,因为 var3 可能 具有与String,例如 InputStream 对象。
    • ...即使 也可以证明(通过遵循程序 by-hand)Any 类型的值将始终是 String.
    • 然而,幸运的是,Kotlin 的 type-checker 是现代的 and its "Smart cast" feature follows the scope of type-narrowing 当使用 is 运算符时,这很整洁(TypeScript 也有,我认为任何其他语言都没有确实如此)。
      • 在您不能使用 Smart-casts 或者可以用其他方式 证明 向下转型是安全的情况下,请使用 as 运算符来执行一个 unsafe cast。像这样:var2: String = var1 as String
        • (有点令人困惑,其他语言使用 as 作为 safe 转换的运算符,argh)。

In context:

fun main() {

    val var1: Any = "Carmelo Anthony"
    val var1Type = var1::class.simpleName
    println("var1's type: " + var1Type) // <-- This will print the *runtime type* of `var1` (String), not its static type (which is `Any`, *not* `String`).

    /*
    val var2: String = var1 // <-- Fails beause `var1` is `Any`, and `Any` is "wider" than `String`, and narrowing conversions always considered unsafe in languages like Kotlin, Java, etc.
    */
    val var2Unsafe: String  = var1 as  String; // <-- Doing this is unsafe because it will throw if `var1` is not a String.
    val var2Safe  : String? = var1 as? String; // <-- Doing this is safe because it `var2Safe` will be null if `var1` is not a String.
    
    println(var2Unsafe)
    println(var2Safe)
}

如果您熟悉其他语言,那么这里有一个不完整的 table 等效操作及其语法:

Kotlin Java JavaScript C# C++
Get static type TypeName::class TypeName.class ConstructorName typeof(TypeName) typeid(TypeName)
Get runtime type variableName::class variableName.getClass() typeof variableName (intrinsics) variableName.constructor (objects) variableName.GetType() typeid(variableName)
Get type from name (string) Class.forName( typeName ).kotlin Class.forName( typeName ) eval( typeName ) (never do this)
Statically-defined runtime type check variableName is TypeName variableName instanceof TypeName typeof variableName === 'typeName' (intrinsics) or variableName instanceof ConstructorName (objects) variableName is TypeName
Runtime dynamic type check otherKClass.isInstance( variableName ) or otherKType.isSubtypeOf() otherClass.isAssignableFrom( variableName.getClass() ) otherType.IsAssignableFrom( variableName.GetType() )
Unsafe narrowing (aka downcast) val n: NarrowType = widerVar as NarrowType; NarrowType n = (NarrowType)widerVar; variableName as TypeName (TypeScript only) NarrowType n = (NarrowType)widerVar;
Safe narrowing (downcast or null) val n: NarrowType? = widerVar as? NarrowType; NarrowType n? = widerVar as NarrowType; dynamic_cast<NarrowType>( widerVar )
Conditional narrowing in scope variableName is TypeName func(x: unknown): x is TypeName guard functions (TypeScript only) widerVar is TypeName n