Kotlin smart cast if

«Умные» преобразования в Kotlin

В этой заметке поговорим про Smart casts или «умные» преобразования.

При некоторых обстоятельствах можно проверить, существуют ли у nullable-типа какое-нибудь значение, кроме null. Если да, то появляется возможность использовать переменную так, как будто она не является null:

1-1801-9d55a1.png

Из кода видно, что при использовании nullable-значения authorName отсутствуют восклицательные знаки. Такое использование nullable-проверок — это пример smart cast либо умного преобразования типов.

Когда nullable-переменная содержит значение, то if-выражение выполняет 1-й блок кода, где Kotlin умным образом преобразует authorName к стандартному не-null типу String. Если же nullable-переменная не содержит значения (null), то выражение if будет выполнять блок else.

Следует отметить, что применение smart cast считается более безопасным, чем утверждение не-null значения посредством оператора . При этом лучше задействовать smart cast всегда, когда у nullable-типа значение может быть null. Ну а утверждение не-null значения подойдет лишь в одном случае — когда nullable гарантированно содержит значение.

Кроме вышесказанного, применение smart cast будет полезным, когда nullable проверен и не будет после этого меняться. Например, когда nullable присваивается переменной, неизменяемой как после умного преобразования, так и перед использованием либо после присвоения константе.

Собственно говоря, знание этих нюансов позволит вам безопасно заглядывать внутрь nullable-типа и извлекать значения, если они там присутствуют.

Источник

Type checks and casts

Use the is operator or its negated form !is to perform a runtime check that identifies whether an object conforms to a given type:

Smart casts

In most cases, you don’t need to use explicit cast operators in Kotlin because the compiler tracks the is -checks and explicit casts for immutable values and inserts (safe) casts automatically when necessary:

The compiler is smart enough to know that a cast is safe if a negative check leads to a return:

or if it is on the right-hand side of && or || and the proper check (regular or negative) is on the left-hand side:

// 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 >

Smart casts work for when expressions and while loops as well:

Note that smart casts work only when the compiler can guarantee that the variable won’t change between the check and the usage. More specifically, smart casts can be used under the following conditions:

  • val local variables — always, with the exception of local delegated properties.
  • val properties — if the property is private or internal or if the check is performed in the same module where the property is declared. Smart casts cannot be used on open properties or properties that have custom getters.
  • var local variables — if the variable is not modified between the check and the usage, is not captured in a lambda that modifies it, and is not a local delegated property.
  • var properties — never, because the variable can be modified at any time by other code.

«Unsafe» cast operator

Usually, the cast operator throws an exception if the cast isn’t possible. And so, it’s called unsafe. The unsafe cast in Kotlin is done by the infix operator as .

Note that null cannot be cast to String , as this type is not nullable. If y is null, the code above throws an exception. To make code like this correct for null values, use the nullable type on the right-hand side of the cast:

«Safe» (nullable) cast operator

To avoid exceptions, use the safe cast operator as? , which returns null on failure.

Note that despite the fact that the right-hand side of as? is a non-null type String , the result of the cast is nullable.

Generics type checks and casts

Please see the corresponding section in the generics documentation page for information on which type checks and casts you can perform with generics.

Источник

Kotlin Type Checks and Smart Casts

Kotlin Type Checks and Smart Casts

When working with mixed types, We often need to know the type of an object at runtime so that we can safely cast the object to our desired type and call methods or access properties on it.

In Kotlin, You can check whether an object is of a certain type at runtime by using the is operator.

Following is an example that demonstrates the usage of is operator.

fun main(args: ArrayString>)  val mixedTypeList: ListAny> = listOf("I", "am", 5, "feet", 9.5, "inches", "tall") for(value in mixedTypeList)  if (value is String)  println("String: '$value' of length $value.length> ") > else if (value is Int)  println("Integer: '$value'") > else if (value is Double)  println("Double: '$value' with Ceil value $Math.ceil(value)>") > else  println("Unknown Type") > > >

In the example above, We have a list containing mixed types. We iterate through the list, check the type of each value in the list using the is operator and print the details about the value.

Following is the output of the above program —

# Output String: 'I' of length 1 String: 'am' of length 2 Integer: 5 String: 'feet' of length 4 Double: 9.5 with Ceil value 10.0 String: 'inches' of length 6 String: 'tall' of length 4 

Note that you can simplify the above program further by replacing the if-else block with a when expression like this —

for(value in mixedTypeList)  when(value)  is String -> println("String: '$value' of length $value.length> ") is Int -> println("Integer: $value") is Double -> println("Double: $value with Ceil value $Math.ceil(value)>") else -> println("Unknown Type") > >

The is operator also has a negated form !is . Here is an example of !is operator —

if(value !is String)  println("Not a String") >

The examples described in the previous section uses a feature of Kotlin called Smart Cast. To understand how Smart Cast work in Kotlin, Let’s compare how we do class casting in Java vs Kotlin.

In Java, We first check the type of the variable using the instanceof operator and then cast it to the target type like this —

Object obj = "The quick brown fox jumped over a lazy dog"; if(obj instanceof String)  // Explicit Casting to `String` String str = (String) obj; System.out.println("Found a String of length " + str.length()); >

But In Kotlin, When you perform an is or !is check on a variable, the compiler tracks this information and automatically casts the variable to the target type in the scope where the is or !is check is true.

val obj: Any = "The quick brown fox jumped over a lazy dog" if(obj is String)  // The variable obj is automatically cast to a String in this scope. // No Explicit Casting needed. println("Found a String of length $obj.length>") >
val obj: Any = "The quick brown fox jumped over a lazy dog" if(obj !is String)  println("Not a String") > else  // obj is automatically cast to a String in this scope println("Found a String of length $obj.length>") >

That’s not all, Smart Casts also work with Short-Circuit operators && and || —

/* obj is automatically cast to String on the right-hand side of "&&" and in the "if" branch */ if(obj is String && obj.length > 0)  println("Found a String of length greater than zero - $obj.length>") >
// obj is automatically cast to String on the right-hand side of "||" if(obj !is String || obj.length > 0)  return >

Note that Smart Casts work only if the compiler can guarantee that the variable hasn’t changed after the is or !is check.

For example, Smart cast doesn’t work for mutable properties of a class. It works only for immutable properties that don’t have a custom getter.

1. Unsafe Cast Operator: as

You can use Kotlin’s Type Cast Operator as to manually cast a variable to a target type —

val obj: Any = "The quick brown fox jumped over a lazy dog" val str: String = obj as String println(str.length)

If the variable can’t be cast to the target type then the cast operator throws an exception. That’s why we call the as operator “Unsafe” —

val obj: Any = 123 val str: String = obj as String // Throws java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

Note that if the variable that you’re trying to cast is nullable then you can’t cast it to a non-null type —

val obj: Any? = null val str: String = obj as String // Throws kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String

The target type needs to be nullable as well for the casting to work —

val obj: Any? = null val str: String? = obj as String? // Works println(str) // Prints null

As you learned in the previous section, the type cast operator as throws ClassCastException at runtime if the casting is not possible.

Kotlin also provides a Safe cast operator as? that returns null instead of throwing a ClassCastException if the casting is not possible —

val obj: Any = 123 val str: String? = obj as? String // Works println(str) // Prints null

Type Check & Smart Cast Example with User Defined Classes and Inheritance

The following example demonstrates Kotlin’s Type Check and Smart Cast concepts using User Defined Classes and inheritance —

open class Animal class Cat : Animal()  fun meow()  println("Meow Meow Meow. ") > > class Dog: Animal()  fun bark()  println("Woof Woof Woof. ") > >
fun main(args: ArrayString>)  val animal: Animal = Cat() if(animal is Cat)  // No explicit casting needed to `Cat` println(animal.meow()) > else if (animal is Dog)  // No explicit casting needed to `Dog` println(animal.bark()) > >

That’s all folks! In this article, You learned how Type Checks and Smart Casts work in Kotlin. You also learned how to use Unsafe and Safe Type Cast Operators for explicitly casting a variable to a target type.

I hope the article was helpful to you. Consider subscribing to our newsletter if you want articles like this to be sent to your inbox whenever I write one.

As always, Thank you for reading. Please ask any doubts in the comment section below.

Источник

Читайте также:  Java new class variable
Оцените статью