In the previous tutorial, we created a simple "Hello world" application. To enhance this it would be useful to query the user for their name to greet them personally. In Kotlin, getting user input is easier than Java, as we can use the readLine()
function. One might infer from this that we could amend our previous application as follows:
fun main(args: Array<String>){
println("Please type your name")
val name = readLine()
val greeting = greet(name)
println(greeting)
}
fun greet(name: String) : String {
return "hello $name"
}
If you were to try and run this code however, it would not compile, and you would see an error the first time you try and use the name variable.
In Java, it is perfectly acceptable to write code such as
String myString = null; // Valid Java
In Kotlin however, we are not permitted to explicitly declare a String as null, thus the following code would be invalidvar myString: String = null //Invalid Kotlin
Kotlin has been designed to avoid a whole class of errors that plague languages like Java, that of unexpected null references. (Chances are by now you will have encountered a NullPointerException when writing Java code). To do this, it prohibits values of objects, (such as String) from being null. Most of the time this will suit us just fine, however on the occasions where there is a valid reason why a value should be null, we need to know how to deal with it.
In the above example, readLine()
can be used to read from either the console or a file on disk, so, when reading from disk, it needs to differentiate between what it returns when it encounters a line of text that had no content (i.e. an empty String), from what it returns when it reaches the end of a file (nothing or null
). The name
variable therefore represents a nullable String. We could explicitly declare it's type as follows:
val name : String? = readLine()
This still doesn't solve our problem above, as we need a regular String to pass into our greet)
method. Fortunately, Kotlin provides a couple of ways to do this:
If we check that a value is not null, we can assign it to a variable of it's non-nullable type. Consider the code below:
if (name != null) {
var greeting = greet(name)
println(greeting)
}
Because the null check is present, Kotlin automatically casts the value from a nullable String to a regular String This allows us to guarantee that the nullable value is actually not null, and convert the type to it's non-nullable equivalent. If we make that guarantee, but cannot be certain that in every possible circumstance the value will not be null, then we will likely end up with a NullPointerException at some point, which is precisely what Kotlin's nullable values are designed to avoid. It should therefore only be used with extreme caution. To perform the conversion, we append the variable or function call with two exclamation marks as in the example below:
var greeting = greet(name!!)
println(greeting)
Modify the initial, non-compiling code so that it works, firstly by using the check for null state and smart cast, then secondly using the non-null assertion operator
Modify the code, so that the greet()
method's name parameter is an optional String, and return a (regular) String with "hello" followed by either the person's name, or "world" if the parameter is null.
Read through the Kotlin Documentation on null safety, and then modify your code for task 2 above, so that it uses the Elvis operator