Kotlin Null syntax, Smart casts & Explicit casting tutorial

Null syntax

The inventor of the quicksort algorithm, who introduced the concept of the null reference in 1965 called it his
“billion dollar mistake”. Unfortunately, we have to live with null references as they are present in the JVM, but Kotlin introduces some functionality to make it easier to avoid some common mistakes.
Kotlin requires that a variable that can assigned to null be declared with a ?:
var str: String? = null

If this is not done, the code will not compile. This next example would result in a compile time error:
var str: String = null
Kotlin has much more than this to help in the fight against null pointer exceptions, and there is a full discussion of nulls and null safety in Lesson 7 ,Null Safety, Reflection, and Annotations.
Type checking and Casting: If a reference to an instance is declared as some general type A,but we want to test if we have a more specific type B, then Kotlin provides the is operator.
This is equivalent to the instanceof operator in Java:

 fun isString(any: Any): Boolean {
      return if (any is String) true else false
    }

If the target type is invalid (a string was trying to be cast to a File), then a ClassCastException will be thrown at runtime.

Smart casts

If after type checking we want to refer to the variable as an instance of B, then the reference must be cast. In Java, this must be done explicitly, which results in duplication:

 public void printStringLength(Object obj) {
      if (obj instanceof String) {
        String str = (String) obj
        System.out.print(str.length())
      }
    }

The Kotlin compiler is more intelligent, and will remember type checks for us, implicitly casting the reference to the more specific type. This is referred to as a smart cast:


    fun printStringLength(any: Any) {
      if (any is String) {
        println(any.length)
      }
    }

The compiler knows that we can only be inside the code block if the variable was indeed an instance of string, and so the cast is performed for us, allowing us to access methods defined on the string instance.
Which variables can be used in a smart cast is restricted to those that the compiler can guarantee do not change between the time when the variable is checked and the time when it is used. This means that var fields and local vars that have been closed over and mutated (used in an anonymous function that assigns a new value) cannot be used in smart casts.
Smart casts even work on the right hand side of lazily evaluated Boolean operations if the left-hand side is a type check:

fun isEmptyString(any: Any): Boolean {
      return any is String && any.length == 0
    }

The compiler knows that in this && expression the right-hand side will not be evaluated unless the left-hand side was
true, so the variable must be a string. The compiler, therefore, smart casts for us and allows us to access the
length property on the right-hand side.Similarly, in a || expression, we can test that a reference is not of a particular type on the left hand side, and if it is it not, then on the right-hand side it must be that type, so the
compiler can smart cast the right-hand side:

 fun isNotStringOrEmpty(any: Any): Boolean {
      return any !is String || any.length == 0
    }

In this example, the function tests that we either don’t have a string, or, if we do, then it must be empty

Explicit casting

To cast a reference to a type explicitly, we use the as operator. Just as in Java, this operation will throw a
ClassCastException if the cast cannot be performed legally:

    fun length(any: Any): Int {
      val string = any as String
      return string.length
    }

The null value cannot be cast to a type that is not defined as null-able. So the previous example would have thrown an exception if the value was null. To cast to a value that can be null, we simply declare the required type as null-able, as we would for a reference:
val string: String? = any as String
Remember that if a cast fails, then a ClassCastException will be thrown. If we want to avoid the exception, and instead have a null value if the cast fails, then we can use the safe cast operator as?. This operator will return the casted value if the target type is compatible,otherwise it will return null. In the next example,string would be a successful cast, but file would be null:


    val any = "/home/users"
    val string: String? = any as String
    val file: File? = any as File
It's only fair to share...Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn