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
::class
operator exists in 2 forms:
TypeName::class
- which returns a KClass
object for the static type TypeName
.variableName::class
- which returns a KClass
object corresponding to the runtime type of variableName
, and not variableName
's static type. (Kotlin calls this the "bound type" in their documentation).var1
has a runtime tytpe of String
but a static type of Any
.
var1::class
returns the KClass
for String
, not Any
.var2
typed as String
, you cannot assign-to var2
from another variable (var3
) statically-typed as Any
, because var3
could have a runtime type that's completely incompatible with String
, e.g. an InputStream
object.
Any
-typed value will always be a String
.is
operator is used, which is neat (TypeScript has it too, I don't think any other language does though).
as
operator to perform an unsafe cast. Like so: var2: String = var1 as String
.
as
as the operator for safe casts, argh).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 |