Kotlin

Type Checks and Casts: 'is' and 'as'

Swift

is and !is Operators

                  if (obj is String) {
    print(obj.length)
}
​
if (obj !is String) { // same as !(obj is String)
    print("Not a String")
}
else {
    print(obj.length)
}
                
                    if (obj is String) {
    print(obj.length)
}
​
if (obj !is String) { // same as !(obj is String)
    print("Not a String")
}
else {
    print(obj.length)
}
                  

Smart Casts

                  fun demo(x: Any) {
    if (x is String) {
        print(x.length) // x is automatically cast to String
    }
}
                
                    func demo(x: Any) {
    if (x is String) {
        print(x.length) // x is automatically cast to String
    }
}
                  
                  if (x !is String) return
​
print(x.length) // x is automatically cast to String
                
                    if (x !is String) return
​
print(x.length) // x is automatically cast to String
                  
                  // x is automatically cast to string on the right-hand side of `||`
if (x !is String || x.length == 0) return
​
// x is automatically cast to string on the right-hand side of `&&`
if (x is String && x.length > 0) {
    print(x.length) // x is automatically cast to String
}
                
                    // x is automatically cast to string on the right-hand side of `||`
if (x !is String || x.length == 0) return
​
// x is automatically cast to string on the right-hand side of `&&`
if (x is String && x.length > 0) {
    print(x.length) // x is automatically cast to String
}
                  
                  when (x) {
    is Int -> print(x + 1)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}
                
                    when (x) {
    is Int -> print(x + 1)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}
                  

"Unsafe" cast operator

                  val x: String = y as String
                
                    let x: String = y as String
                  
                  val x: String? = y as String?
                
                    let x: String? = y as String?
                  

"Safe" (nullable) cast operator

                  val x: String? = y as? String
                
                    let x: String? = y as? String
                  

Type erasure and generic type checks

                  if (something is List<*>) {
    something.forEach { println(it) } // The items are typed as `Any?`
}
                
                    if (something is List<*>) {
    something.forEach { print(it) } // The items are typed as `Any?`
}
                  
                  fun handleStrings(list: List<String>) {
    if (list is ArrayList) {
        // `list` is smart-cast to `ArrayList<String>`
    }
}
                
                    func handleStrings(list: List<String>) {
    if (list is ArrayList) {
        // `list` is smart-cast to `ArrayList<String>`
    }
}
                  
                  inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
    if (first !is A || second !is B) return null
    return first as A to second as B
}
​
val somePair: Pair<Any?, Any?> = "items" to listOf(1, 2, 3)
​
val stringToSomething = somePair.asPairOf<String, Any>()
val stringToInt = somePair.asPairOf<String, Int>()
val stringToList = somePair.asPairOf<String, List<*>>()
val stringToStringList = somePair.asPairOf<String, List<String>>() // Breaks type safety!
                
                    inline func <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
    if (first !is A || second !is B) return null
    return first as A to second as B
}
​
let somePair: Pair<Any?, Any?> = "items" to [1, 2, 3]
​
let stringToSomething = somePair.asPairOf<String, Any>()
let stringToInt = somePair.asPairOf<String, Int>()
let stringToList = somePair.asPairOf<String, List<*>>()
let stringToStringList = somePair.asPairOf<String, List<String>>() // Breaks type safety!
                  

Unchecked casts

                  fun readDictionary(file: File): Map<String, *> = file.inputStream().use { 
    TODO("Read a mapping of strings to arbitrary elements.")
}
​
// We saved a map with `Int`s into that file
val intsFile = File("ints.dictionary")
​
// Warning: Unchecked cast: `Map<String, *>` to `Map<String, Int>`
val intsDictionary: Map<String, Int> = readDictionary(intsFile) as Map<String, Int>
                
                    func readDictionary(file: File): Map<String, *> = file.inputStream().use { 
    TODO("Read a mapping of strings to arbitrary elements.")
}
​
// We saved a map with `Int`s into that file
let intsFile = File("ints.dictionary")
​
// Warning: Unchecked cast: `Map<String, *>` to `Map<String, Int>`
let intsDictionary: Map<String, Int> = readDictionary(intsFile) as Map<String, Int>
                  
                  inline fun <reified T> List<*>.asListOfType(): List<T>? =
    if (all { it is T })
        @Suppress("UNCHECKED_CAST")
        this as List<T> else
        null
                
                    inline func <reified T> List<*>.asListOfType(): List<T>? =
    if (all { $0 is T })
        @Suppress("UNCHECKED_CAST")
        this as List<T> else
        null