val var1: Any = "Carmelo Anthony"
I'm under the impression ::class.simpleName returns the variable type of an object
when I do the following:
val var1Type = var1::class.simpleName
print(var1Type)
I get String and not Any
but when I do this
val var2: String = var1
I get a Type mismatch: inferred type is Any but String was expected
CodePudding user response:
- In Kotlin, the
::classoperator exists in 2 forms:TypeName::class- which returns aKClassobject for the static typeTypeName.variableName::class- which returns aKClassobject corresponding to the runtime type ofvariableName, and notvariableName's static type. (Kotlin calls this the "bound type" in their documentation).
- In your case,
var1has a runtime tytpe ofStringbut a static type ofAny.- So
var1::classreturns theKClassforString, notAny.
- So
- But Kotlin's type system, like most statically typed languages, does not allow for implicit narrowing conversion (i.e. given a variable
var2typed asString, you cannot assign-tovar2from another variable (var3) statically-typed asAny, becausevar3could have a runtime type that's completely incompatible withString, e.g. anInputStreamobject.- ...even if it's provable (by following the program by-hand) that the
Any-typed value will always be aString. - Fortunately, however, Kotlin's type-checker is modern and its "Smart cast" feature follows the scope of type-narrowing when the
isoperator is used, which is neat (TypeScript has it too, I don't think any other language does though).- In situations where you can't use Smart-casts or can otherwise prove to yourself that a downcast is safe then use the
asoperator to perform an unsafe cast. Like so:var2: String = var1 as String.- (Somewhat confusingly, other languages use
asas the operator for safe casts, argh).
- (Somewhat confusingly, other languages use
- In situations where you can't use Smart-casts or can otherwise prove to yourself that a downcast is safe then use the
- ...even if it's provable (by following the program by-hand) that the
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)
}
If you're familiar with other languages, then here's an incomplete table of equivalent operations and their syntax:
| 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 |
