KotlinCS 124 LogoJava
PrevIndexNext
Kotlin
Java
  • Implementing a Map : 04/26/2024

  • map-reduce-filter : 04/25/2024

  • Generics : 04/24/2024

  • Hashing : 04/23/2024

  • Binary Search : 04/22/2024

  • MP3: Course Ratings : 04/19/2024

  • Quicksort : 04/18/2024

  • Merge Sort : 04/17/2024

  • Sorting Algorithms : 04/16/2024

  • MP Debugging Part 1 : 04/15/2024

  • MP2: Course Activity : 04/12/2024

  • Practice with Recursion : 04/11/2024

  • MP Debugging Part 0 : 04/10/2024

  • MP2: API Client : 04/09/2024

  • MP2: API Server : 04/08/2024

  • Trees and Recursion : 04/05/2024

  • Trees : 04/04/2024

  • Recursion : 04/03/2024

  • MP1: Filtering and Search : 04/02/2024

  • MP1: Loading and Sorting : 04/01/2024

  • Lists Review and Performance : 03/29/2024

  • Linked Lists : 03/28/2024

  • Algorithms and Lists : 03/27/2024

  • Continuing MP0 : 03/26/2024

  • Getting Started with MP0 : 03/25/2024

  • Lambda Expressions : 03/22/2024

  • Anonymous Classes : 03/21/2024

  • Practice with Interfaces : 03/20/2024

  • Implementing Interfaces : 03/19/2024

  • Using Interfaces : 03/18/2024

  • Working with Exceptions : 03/08/2024

  • Throwing Exceptions : 03/07/2024

  • Catching Exceptions : 03/06/2024

  • References and Polymorphism : 03/05/2024

  • References : 03/04/2024

  • Data Modeling 2 : 03/01/2024

  • Equality and Object Copying : 02/29/2024

  • Polymorphism : 02/28/2024

  • Inheritance : 02/27/2024

  • Data Modeling 1 : 02/26/2024

  • Companion Objects : 02/23/2024

  • Encapsulation : 02/22/2024

  • Constructors : 02/21/2024

  • Objects, Continued : 02/20/2024

  • Introduction to Objects : 02/19/2024

  • Compilation and Immutability : 02/16/2024

  • Practice with Collections : 02/15/2024

  • Maps and Sets : 02/14/2024

  • Lists and Type Parameters : 02/13/2024

  • Imports and Libraries : 02/12/2024

  • Multidimensional Arrays : 02/09/2024

  • Practice with Strings : 02/08/2024

  • null : 02/07/2024

  • Algorithms and Strings : 02/06/2024

  • Strings : 02/05/2024

  • Functions and Algorithms : 02/02/2024

  • Practice with Functions : 02/01/2024

  • More About Functions : 01/31/2024

  • Errors and Debugging : 01/30/2024

  • Functions : 01/29/2024

  • Practice with Loops and Algorithms : 01/26/2024

  • Algorithms : 01/25/2024

  • Loops : 01/24/2024

  • Arrays : 01/23/2024

  • Compound Conditionals : 01/22/2024

  • Conditional Expressions and Statements : 01/19/2024

  • Operations on Variables : 01/18/2024

  • Variables and Types : 01/17/2024

  • Welcome to CS 124 : 01/16/2024

References

class Person(var name: String)
val me = Person("Geoff")
val you = me
you.name = "Fantastic Student"
println(me.name)

Here we begin a series of lessons that will deepen our understanding of Kotlin. They will also introduce an incredibly important and prevalent idea in computer science—references. So let’s get started!

A Puzzle
A Puzzle

As we frequently do, we’ll begin with a puzzle. Examine the following code (duplicated from the lesson header) and try to predict what should happen:

class Person(var name: String)
val me = Person("Geoff")
val you = me
you.name = "Fantastic Student"
println(me.name)

The critical bit is the line val you = me. There are two things that could happen here:

  1. Kotlin could make a copy of me and save it to you, meaning that at that point there would be two Person objects; or
  2. Something else…

Given the result, the answer is clearly the latter. On some level we should find this reassuring, since previously we had said that (1) objects are only created when you see the constructor called and (2) Kotlin does not have a built in way of making object copies. However, it does leave us with the question: what is happening, and why?

References
References

Until this lesson we’ve been somewhat vague about exactly what is being stored in our variables. No longer. Variables in Kotlin that store objects do not store the object instance itself. Rather, they store a reference to the instance.

What is a reference? I’m glad you asked! Wikipedia says.

In computer science, a reference is a value that enables a program to indirectly access a particular datum, such as a variable’s value or a record, in the computer’s memory or in some other storage device. The reference is said to refer to the datum, and accessing the datum is called dereferencing the reference.

Let’s parse this together:

Kotlin Object Variables Store References
Kotlin Object Variables Store References

In Kotlin, when we assign an instance of an object to a variable, what we are really doing is creating a reference variable. The variable stores a reference to the object, not the object itself:

class Person(val name: String)
val s = Person("You") // s stores a reference to a Person with name "You"
val t = s // t now also stores a reference to the _same_ Person
println(t.name)

If and when you get lost here, here’s something that will help. Objects are only created when you see the constructor called. In the example above, even though we have two reference variables (s and t), we have only one Person created when we see the constructor called.

References Are Not What They Refer To
References Are Not What They Refer To

A reference is not the thing it refers to. This becomes clear when we examine some real-world examples of references:

In each of these cases above, a reference is something that we can use to access the object it refers to. If I give you my phone number, you can call me. If I give you my address, you can visit! If I make a bunch of copies of my address, you could all come over and visit! But I would still only have one house.

Changes to Objects Are Visible to Reference Holders
Changes to Objects Are Visible to Reference Holders

Let’s continue the analogy above. Imagine I give two of you the address to my house. The next day, one of you comes by and “decorates” my house with sanitary paper. If the other comes by later, they also see the change!

class House(var hasTP: Boolean = false)
val first = House()
val second = first
println(first.hasTP)
second.hasTP = true // Please don't actually do this!
println(first.hasTP)

So the next rule of references: changes to object instances are visible to all reference holders. This is why, in the example above, me.name returns “Fantastic Student” even though the change was made using the reference variable you. Both refer to the same object, the only one that was created by calling the constructor.

Primitive Types Store Values
Primitive Types Store Values

Note that all that we said above is not true for Kotlin basic types. They store their values directly in the variable:

var i = 8
var j = i // The value from i is copied into j
j++
println(j)
println(i)

But this only works for the eight basic types. Any variable that stores an object in Kotlin is a reference variable, and actually stores an object reference.

null is a Reference
null is a Reference

Finally, this also gives us a better understanding of null. null is the empty reference:

var s: String? = null

null indicates that a variable that could store a reference does not, and is empty. This also explains what happens if we try and follow or deference null. (Imagine if I told you to call a phone number, but then handed you a blank sheet of paper!)

Reference Practice
Reference Practice

Let’s get some practice manipulating references.

Copying Reference
Copying Reference

Copying a reference does not copy the object it refers to! Let’s return to our example and discuss what is actually happening:

class Person(var name: String)
val me = Person("Geoff")
val you = me
you.name = "Fantastic Student"
println(me.name)

Now, let’s go through this step by step using a diagram:

Swapping References
Swapping References

Imagine I have the following code, and I want to swap the objects that me and you refer to. (Searching for the fountain of youth, I guess…) Let’s walk through how we would do that:

class Person(var age: Int)
var me = Person(41)
var you = Person(18)

Next, let’s examine a similar example using a diagram:

Reference Equality v. Instance Equality
Reference Equality v. Instance Equality

In Kotlin, == calls .equals and so always tests instance equality as defined by the class. However, what if we need to test reference equality? There’s also a way to do that! Let’s see how.

class Example(val value: Int) {
override fun equals(other: Any?): Boolean {
return when {
other?.javaClass != javaClass -> false
else -> {
other as Example
other.value == value
}
}
}
}
val first = Example(88)
val second = Example(88)
val third = first
println(first == second)
println(first === second)
println(first == third)
println(first === third)

However! One important caveat is that === only works the way you expect if the values are not Kotlin basic types. For example:

val firstString = "one"
val secondString = firstString
println(firstString === secondString)
val firstInt = 10
val secondInt = 10
println(firstInt === secondInt)

Practice: Object v. Reference Equality

Created By: Geoffrey Challen
/ Version: 2020.10.0

Implement a method named compare that accepts two Any? arguments. It should return 0 if both references are equal, 1 if both objects are equal, and -1 otherwise. Either reference can be null, so you'll need to handle those cases carefully!

Practice: Mystery Method 1

Created By: Geoffrey Challen
/ Version: 2020.10.0

Today's homework problem is a bit different. There is no description! Instead, you should use our testing suite and its error messages to help you figure out what to do! This is also known as reverse engineering. Good luck, and have fun!

Homework: Mystery Method 2

Created By: Geoffrey Challen
/ Version: 2021.9.0

This homework problem is a wee bit different. There is no description! Instead, you should use our testing suite and its error messages to help you figure out what to do! This is also known as reverse engineering. Good luck, and have fun!

The only hint that we'll provide for Kotlin is that you should write a top-level method, not define a class.

More Practice

Need more practice? Head over to the practice page.