KotlinCS 124 LogoJava

null

var test: String? = null
println(test.length())

This lesson is all about nothing! It turns out that nothing can cause us some trouble! But, because we’re using Kotlin, we have a language designed around dealing with nothing safely. Let’s try to make sense of those statements, and also continue to practice writing String algorithms.

Java’s Nothing That Is (A Big Problem)
Java’s Nothing That Is (A Big Problem)

Wait: why are we suddenly talking about Java?

To understand how Kotlin handles null, we need to take a brief digression and discuss a different programming language: Java. Why? Because Kotlin is designed to improve on but also to interoperate with Java code. So in some ways null is a problem that Kotlin inherits from Java, even if Kotlin provides us with much better solutions for working with this special value safely.

Java has a special value that is used to indicate that an object is uninitialized: null. Here’s an example using Strings, one of the objects we have been working with:

String s = null;

Any Java object can be initialized to null. Because Kotlin interoperates with Java, this is where the problem creeps in…

The Billion Dollar Mistake
The Billion Dollar Mistake

null seems harmless and maybe even kind of cute! But it has left a trail of damage and tears in its wake. In fact, famous computer scientist Tony Hoare, who is credited for inventing null as part of the programming language ALGOL, refers to it as his “billion dollar mistake”. Let’s look at why. Again, this example examines Java code: we’ll get back to Kotlin in just a minute.

String s = null;

Kotlin to the Rescue
Kotlin to the Rescue

null has caused so many problems over the years, that Kotlin makes working with null safely a core design goal.

One of the ways that it does it does this is that, unlike Java, Kotlin carefully tracks what variables can and can’t hold null in our programs. Consider the following example:

var value = "Testing"
value = "Changed" // This works
value = null // This does not

If you try to run the code above you’ll see that it fails. The reason is that, while value can store Strings, and we can change its value to another String, it cannot store null. So, for each variable in our program, Kotlin knows whether or not it can hold null and will fail if we try to assign null to a variable that can’t contain it.

Sometimes null is a useful value in our programs to indicate that a variable is unset or uninitialized. So: what if we want a String that can contain null? Here’s how we do that:

var value: String? = "Testing"
println(value)
value = null
println(value)

Note how we used the type String? on the variable value. By appending an ? to any type name in Kotlin we indicate that the variable can also store null. You can think of the ? as hinting that maybe the variable stores the type, but maybe it stores null (nothing)!

This also works for the other kinds of variables we have been using:

var value: Int? = 5
value = null
println(value)

Types that end with ? and can contain null are referred to as nullable in Kotlin.

Because null can be used to initialize any value, Kotlin’s type inference fails if we try and initialize a value with null:

var value = null
value = 5

A value that is initialized with null has the inferred type Nothing?, which is fairly useless since a Nothing? variable can’t store anything but null! Instead, when we need a nullable variable we will need to specify the type explicitly, even if we initialize it with a value:

var first = 3 // Inferred type is Int, non-nullable
var second: Int? = 5 // Inferred type is Int?, nullable
first = null // fails
second = null // OK

Safely Working with null
Safely Working with null

From this point forward we’re going to try and keep null in the back of our minds. Always. Whenever we have a variable that could be null, we need to make sure that it isn’t null before we do anything with it!

Happily Kotlin provides us with a lot of help here! We’ll get to know Kotlin’s null safely capabilities bit-by-bit as we go. But here’s a few tips to get us started.

When declaring a method we have two options. First, we can prevent the method from accepting null by using a non-nullable type:

fun countCharacters(input: String, toCount: Char): Int {
// Neither input nor toCount can be null, hooray!
var count = 0
for (character in input.toCharArray()) {
if (character == toCount) {
count++
}
}
return count
}
println(countCharacters(null, 'X')) // Fails since input is non-nullable

Alternatively, if the method must accept a nullable type, we can simply check for it at the beginning. Let’s see how to do that:

fun countCharacters(input: String?, toCount: Char?): Int {
}

Safe Property Access
Safe Property Access

One of the biggest problems with null in Java is errors caused by trying to use dot-notation on a variable that contains null: (Again, this is Java code.)

String s = null;
System.out.println(s.length());

In Kotlin, whenever a variable could be null, plain dot notation will not work:

var s: String? = null
println(s.length) // Won't run, since s could be null

Instead, we have to use something called the safe call operator. Let’s see how that works:

var s: String? = null
println(s.length) // Won't run, since s could be null

Practice: Array Max (1D)

Created By: Geoffrey Challen
/ Version: 2020.9.0

Declare and implement a function called arrayMax. It should accept a nullable DoubleArray as its single argument, and return the maximum value stored in the array. If the array is empty or null, you should return 0.0.

More Practice With Strings
More Practice With Strings

Now let’s continue developing our algorithmic and String manipulation capabilities. Let’s apply our skills to determining whether two Strings are anagrams. An anagram is created by rearrange the letters from one word to form another:

For our implementation we will not ignore whitespace and capitalization. Some anagrams do: for example, “New York Times” and “monkeys write” are anagrams, but the first string has two spaces while the second has only one. To us those would not be anagrams. When we are done, we can discuss how to make our approach more flexible.

// Design and implement an anagram method

Note that there are better ways to implement this algorithm. Perhaps we’ll return to it later and experiment with one or even two alternate approaches. That’s part of what makes computer science so exciting! There is always more than one way to solve any problem…

Practice: Array Count Greater Than 1D

Created By: Geoffrey Challen
/ Version: 2021.8.0

Write a method named arrayCountGreaterThan. It should accept a non-nullable Int array as its first argument and an Int as its second, and return a count of how many values in the array are strictly greater than the second parameter.

Homework: Array All Multiples

Created By: Geoffrey Challen
/ Version: 2022.8.0

Write a method named arrayAllMultiples that, given an array of Int values greater than 0, returns whether the values are all integer multiples of one of the values in the array, which we'll call the base. For example, given the array {4, 2, 8} you would return true, since 4 and 8 are integer multiples of the base 2. Given the array {2, 2, 5} you would return false, since there is no value that all others are integer multiples of. (If the array contains 1, you should always return true, but there's no need to handle this case specifically.)

You should approach this problem in two steps. First, identify the base—the value in the array that you are going to check whether others are multiples of. This may sound complicated, but it's straightforward. You do not need to and should not check whether every value is base! Your solution should not contain a nested loop.

Next, check all the values in the array and look for a counterexample—a value that is not a multiple of the base.

The passed array may be null or empty. In those cases you should return false. Valid arrays will contain only integers strictly greater than 0.

ACM Student Chapter
ACM Student Chapter

Hello! We are ACM@UIUC, the largest CS RSO on campus! We represent 16+ special interest groups that focus on all areas of computing—from security, to AI, to policy. Each special interest group hosts weekly meetings on their topics of choice. Some are educational, others are experimental, and most are just plain fun. We also have 7 committees that maintain our internal state and host activities; among them are HackIllinois and R|P, the largest student-run hackathon and student-led tech conference in the Midwest, respectively.

You can learn more about our ACM chapter here.

More Practice

Need more practice? Head over to the practice page.