Instruction 2
Using the Elvis Operator
The Elvis operator (?:) is a convenience operator that enables you to check and assign a value to a variable if it’s not null. This is a cool feature that could save you some lines of code. Without it, your code could look like this:
fun main() {
val items: Int? = null
var amount = 0
if (items != null){
amount = items
}
println("Amount to pay: $amount")
}
Run the code and check the results:
Amount to pay: 0
The previous example contains three extra lines of code. Not very clean. Sometimes, though, it may be good for clarity, depending on who reads the code.
Use the Elvis operator to update your code to something more idiomatic to Kotlin:
fun main() {
val items: Int? = null
val amount = items ?: 0
println("Amount to pay: $amount")
}
Run the code and check the results. You have fewer lines of code for the same output:
Amount to pay: 0
Using the Null Safe Operator
The null-safe operator is symbolized by ?.. Instead of using the dot notation . to access methods or fields on an object, the null-safe operator makes use of ?. This then ensures that the method call only happens if the object isn’t null. If it is, the method isn’t invoked.
In the following example, you’ll use the plus method to add the number of apples to the number of oranges in a null-safe manner. You need to do so because the number of apples is nullable:
fun main() {
val apple: Int? = null
val orange: Int = 5
val total = apple?.plus(orange)
println(total)
}
Run the code and check the results:
null
The total is null because the number of apples was null.
Let apple remain nullable, but initialize it with a valid number this time:
fun main() {
val apple: Int? = 5
val orange: Int = 5
val total = apple?.plus(orange)
println(total)
}
Run the code. This time, the total is 10 because apple wasn’t null:
10
What if you had to call the method on a non-nullable object instead while taking the nullable as an operand? For that, you’ll need let.
Introducing Let and Run Scope Functions
The let scope function behaves similarly to the null-safe ?. operator. In that, it only executes if the object isn’t null. The key difference here is that if it’s not null, it makes the object available in the let function for further use. The former doesn’t offer this capability. Make the following modifications to the previous example to use let:
fun main() {
val apple: Int? = 5
val orange: Int = 5
val total = apple?.let { orange.plus(it) }
println(total)
}
it within the plus() function is the default variable name for the let function. It holds the value of apple if apple isn’t null.
Similar to let, is the scope function run. The primary difference between let and run is that the this object is used in place of the it variable.
Whenever you declare a variable, Kotlin forces you to initialize it. If it’s a nullable type, you can simply initialize it with a null. If it’s not, Kotlin provides the notNull delegate to handle such situations.
Understanding the NotNull Delegate
A delegate is a software design pattern where an object delegates its duties to another object. The notNull delegate allows a variable to be declared as non-nullable but not during initialization. It must be a mutable variable since you’ll have to provide its value later on in the program.
import kotlin.properties.Delegates
var items by Delegates.notNull<Int>()
fun main() {
items = 5
println(5)
}
items is non-nullable. If you attempt to assign a nullable to it, you’ll get an error, and your program won’t compile.
Note: Failing to initialize the variable before using it will result in an
IllegalStateException.
Note: Kotlin compiles your code before running it. Compile time errors are errors raised while your program compiles. This type of error is good because it affords you the chance to know where a problem is and to fix it before releasing your program.
The notNull delegate is suitable for primitive values like Int, String, and others like them. For non-primitive or custom types, you can use the lateinit modifier.
Working with the Lateinit Modifier
This modifier is added to a variable that will be initialized later on in the program, as opposed to at the time of declaration. It behaves similarly to Delegate.notNull() above.
In the next example, you’ll create a custom Book type to test the lateinit modifier:
lateinit var book: Book
fun main() {
book = Book()
book.display()
}
class Book {
fun display(){
println("lateinit modifier works similar to Delegate.notNull()")
}
}
Run the code. See the results in the console:
lateinit modifier works similarly to Delegate.notNull()
If you attempt to make the type, Book in this case, nullable or attempt to assign a nullable to book later on in the program, it’ll raise a compile-time error.
When you’re certain that a nullable type will contain a non-null value, Kotlin offers !!. This operator allows you to access the variable as a non-null type and without the ?. operator.