Instruction 3
The Difference Between Logical and Comparison Operators
Both logical and comparison operators result in a Boolean value, true or false. Logical operators operate on Boolean values only. Comparison operators operate on both Boolean and numerical data types.
The following operators operate on nullable values. The first on the list is ?.
The ? Operator
This operator marks a variable or object as nullable. This means the variable or object can contain a null value. Without it, the value is non-nullable. That means there’s no way it can be null. This assurance is essential for dealing with values. Kotlin forces you to assign only non-nullable values to non-nullable types. If a type is nullable, you may assign a null or non-null value to it.
To make a variable nullable, append ? to the type in the declaration. This is how you make a variable nullable:
fun main() {
val items: Int? = null
println(items)
}
The Elvis Operator
This is a special Kotlin operator, ?:, that assigns the value to the left of the operator to a variable if the value isn’t null. Otherwise, it assigns the value to the right of the operator. null is a special data type that means there’s nothing. When you try to operate on a null value, you’ll get an error. There’s more about this in Lesson 5.
In the example below, you pay for the items if it isn’t null. If it is, you pay 0. Here’s how it works:
fun main() {
val items = null
val amount = items ?: 0
println("Amount to pay: $amount")
}
Run the code and check the results:
Amount to pay: 0
The amount was assigned 0 because the items were null, which means there were no items to pay for. If there were, the amount would be assigned the value of the items.
Note: The Elvis operator, when viewed from the right, looks like the entertainer’s trademark hairstyle:
?:. Thank you. Thank you very much. :]
The ?. Operator
You’re still on nulls. Remember that calling a method on a null object will result in an error? As part of Kotlin’s type safety mechanisms, the null-safe operator ?. is used to access null objects safely. When used, the method will only be called if the variable is not null. If it is, that portion of code won’t be executed.
fun main() {
val apple: Int? = null
val orange: Int = 5
val total = apple?.plus(orange)
println(total)
}
Run the code, and you get null. Since apple was null, it wasn’t added to the number of oranges.
The !! Operator
This operator asserts that an expression isn’t null. The variable may be nullable, but you may want to call methods on it. Instead of using the null safe operator ?., you can use the null assertion operator to cast the nullable type to a non-nullable variable. This then allows you to call methods on the variable as though it were non-nullable.
This is how to use it:
fun main() {
var fruit: String? = null
fruit = "Sugarcane"
println(fruit!!.uppercase())
}
Run the code, and you get SUGARCANE, the upper case or all caps version of Sugarcane.
Note: The Kotlin Playground IDE shows two warnings with the above code. More complex code examples are needed to generate a realistic case for the
!!operator. Still, be aware of your IDE when it gives helpful hints.
Note: As a rule of thumb, bear in mind that
!!operator is considered a code smell. Try to avoid at all cost.
The in Operator
The in operator is used to check if an operand contains another operand. This operator works on sequences and collection. The operation searches through the operand on the right for the value of the operand on the left. Non-primitive values are values made of basic data types. An array of strings is a non-primitive value. Non-primitive values have the method contains() instead of in.
Run the following few examples:
fun main() {
val bucketList = arrayOf("Kotlin", "Android", "iOS", "Flutter", "Kodeco")
if ("Kotlin" in bucketList){
println("Yes! Kotlin is in my bucket list.")
}
}
Run the code and the output is:
Yes! Kotlin is in my bucket list.
Or you could use the contains() function for the same output:
fun main() {
val bucketList = arrayOf("Kotlin", "Android", "iOS", "Flutter", "Kodeco")
if (bucketList.contains("Kotlin")){
println("Yes! Kotlin is in my bucket list.")
}
}
Note that with the contains(), the operand you’re performing the search on is on the left-hand side of the function.
To negate the expression, use prepend the operator with the NOT operator !:
fun main() {
if ("I" !in "Team"){
println("There's no I in 'Team'.")
}
}
There's no I in 'Team'.
“There’s an ‘I’ in Win!” -Michael Jordan :] OK, moving on….
The following operators work with collections, sequences and ranges.
Indexed Access Operator
This is represented by two square brackets []. It’s used to access elements in a collection. Used with a key-value-based type, it receives a value within the square brackets that corresponds with the key.
fun main() {
val romanNumerals = mapOf("IX" to 9)
println(romanNumerals["IX"])
}
Run the code. It prints:
9
Note: If you attempt to access a key that’s not available, you’ll get
null. Also, if you try to access a key with a type different from the type for the variable’s keys, you’ll get an error that says:
The character literal does not conform to the expected type Int # Or whichever the correct type is.
For collections, the indexing is zero-based. Starting from 0, you can access the first item in the collection to the last item:
fun main() {
val romanNumerals = arrayOf("I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X")
println(romanNumerals[5])
}
Run the code. It prints:
VI
Note: Accessing an index outside the available range results in an
ArrayIndexOutOfBoundsException.println(romanNumerals[15]would give the result:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 15 out of bounds for length 10
at FileKt.main (File.kt:3)
at FileKt.main (File.kt:-1)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (:-2)
The Range Operator
The range operator is defined by two dots ... It creates a range of values that follow a natural progression from the left operand to the right operand inclusive.
The following example creates a range from 1 to 10:
fun main() {
for (i in 1 .. 5){
println(i)
}
}
Run it. And it prints:
1
2
3
4
5