Kotlinlearncs.online LogoJava

References and Polymorphism

open class Person {
fun speak() { }
}
class Student : Person() {
fun learn() { }
}
class Professor : Person() {
fun teach() { }
}
fun participate(person: Person) {
person.speak()
person.learn()
person.teach()
}
val student = Student()
val professor = Professor()
participate(student)
participate(professor)

This lesson combines what we’ve learned previously about polymorphism with what we now know about references. We’ll examine how the type of a reference variable—not the type of what it refers to—determines what methods and field we can access using dot notation. We’ll also use this opportunity to reinforce our developing view of references.

Reference v. Instance Type
Reference v. Instance Type

When we introduced polymorphism and “is a” relationships, we introduced how the type of a variable and the type of the object stored in it could differ:

val o: Any = "test"

Now, using our more precise terminology, we can be more clear about exactly what is going on here:

open class Person {
fun speak() { }
}
class Student : Person() {
fun learn() { }
}
class Professor : Person() {
fun teach() { }
}
fun participate(person: Person) {
person.speak()
person.learn()
person.teach()
}
val student = Student()
val professor = Professor()
participate(student)
participate(professor)

Dot Notation
Dot Notation

One additional piece of Kotlin syntax that should now become more clear is dot notation. We’ve been using it to access object fields and methods:

val s = "88"
println(s.length)

Now we know what is happening! The . causes Kotlin to follow the reference variable to the object it refers to: in this case it follows s to the String we created on the line above. If we create two references that lead to the same place, it will follow either:

val s = "88"
val t = s
println(s.length)
println(t.length)

More Reference Examples
More Reference Examples

With a bit more information under our belt, let’s go over a few more important examples of places where references are used.

Pass by Reference
Pass by Reference

When a method is called in Kotlin, what is passed to the method is a reference, not a copy of the object. This allows the method to modify the object. Let’s look at how this works!

class Example(var value: Int = 0)
fun increment(example: Example) {
example.value++
}
val e = Example()
println(e.value)
increment(e)
println(e.value)

And now, using a diagram to make the relationships clear visually:

Arrays Store References
Arrays Store References

Kotlin arrays that store objects actually store object references. This has some important implications. Let’s look at an example:

class Person(var age: Int)

Practice: Object Array Shallow Copy

Created By: learncs.online Staff
/ Version: 2020.10.0

Create a method named copy that accepts a nullable array of Any?s. It should return a shallow copy of the passed array: meaning that the returned array should contain references to the same objects in the passed array. If the passed array is null, copy should return null.

Generality v. Capability
Generality v. Capability

Some of you might be wondering: why would I ever write a method that accepts Any? It seems like you lose so much information about the object by doing that!

While that is true, the tradeoff is with generality. Let’s try and make that clear through an analogy:

Homework: List Unique Items

Created By: learncs.online Staff
/ Version: 2021.9.0

Create a method uniqueItems. uniqueItems accepts a list of Anys and returns a count of how many of the objects in the list are unique, meaning that there are no other objects in the list that are equal to them.

For example, given the list {1, 2, 4} you would return 3, whereas given the list {2, 2, 5} you would return 1. The list may contain any kind of Any. You may use collections like Maps or Sets for this problem, but they are not required.

More Practice

Need more practice? Head over to the practice page.