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

Companion Objects

class Math {
companion object {
const val PI = 3.141597
fun pow(base: Int, exponent: Int): Int {
assert(exponent >= 0) { "No support for negative exponents" }
var result = 1
for (i in 0 until exponent) {
result *= base
}
return result
}
}
}
println(Math.PI)
println(Math.pow(2, 3))

As we continue, we begin by describing a feature of Kotlin objects called companion objects. We’ll also introduce introduce one new piece of object syntax.

Warm Up Debugging Challenge
Warm Up Debugging Challenge

But first, let’s warm up with another graded debugging challenge!

this
this

this is a keyword in Kotlin that you can use in your instance methods. It always refers to the current instance that is executing the method.

So this:

class Course(var number: String) {
fun changeNumber(newNumber: String) {
number = newNumber
}
}

is equivalent to this:

class Course(var number: String) {
fun changeNumber(newNumber: String) {
this.number = newNumber
}
}

The example above is one use of this. However, we’ll usually just go the first route, and choose parameter names that don’t conflict with our instance variable names. This helps avoid mistakes.

However, there is one place where we do and will use this. Let’s go through it together:

class Course(var number: String)

Companion Objects
Companion Objects

Up until now the properties and methods that we’ve been establishing on our Kotlin objects are instance properties and methods. Meaning each instance of an class has its own:

class Person(val name: String) {
fun doubleName(): String {
return name + name
}
}
val first = Person("Geoff")
val second = Person("Chuchu")
println(first.name)
println(second.name)
println(first.doubleName())

Even though they share an implementation of doubleName, instances each act like they have their own doubleName method.

However, Kotlin also allows us to create methods that are provided by the class and can be accessed without an instance. We do this using something called a companion object. Let’s see how:

class Person(val name: String)

Companion v. Instance
Companion v. Instance

Companion object methods cannot access instance variables. Let’s look at why, and the differences between class and instance methods:

class Person(val name: String) {
companion object {
fun getName(): String {
return name
}
}
}
val student = Person("You")
println(student.getName())

Uses for Companion Objects
Uses for Companion Objects

In Kotlin, we can create methods that are not associated with any class, simply by declaring them outside a class body:

class Person(val name: String)
fun greetPerson(person: Person) {
println("Hello ${person.name}!")
}
val geoff = Person("Geoff")
greetPerson(geoff)

This works completely fine, and limits the degree to which we need to use companion objects. So if you have a method that doesn’t need an instance, you can either declare it outside the class or in a companion object.

Practice: Static Adder

Created By: Geoffrey Challen
/ Version: 2020.9.0

Create a class named Math. Math should declare a companion object with a method named add. add should accept two Int arguments and return their sum.

Companion Fields
Companion Fields

Companion objects can also declare fields, just like instances:

class Person(val name: String) {
companion object {
val typicalGreeting = "Hello"
}
}
println(Person.typicalGreeting)

This can be a good place to put constant values, as shown above, particularly if, like a method, they really belong with the class that defines the companion object.

You can also define non-constant (i.e., var) properties on companion objects. However. This is extremely rare, and very easy to get wrong. So much so that we won’t bother demonstrating how to do it!

Practice: Toggler Object

Created By: Geoffrey Challen
/ Version: 2021.9.0

Define a class named Toggler with a single instance method named toggle that takes no parameters and returns a Boolean. Toggler should also provide a primary constructor that accepts a Boolean argument and sets the initial state of the Toggler instance.

Toggler maintains one piece of private state: the Boolean. Calling toggle changes the Boolean from true to false or false to true and returns the new (not the old) state of the Boolean. So, for example:

Note that the internal state should be private.

Homework: Stepper Object

Created By: Geoffrey Challen
/ Version: 2022.9.0

Define a public class named Stepper with a single instance method named next that takes no parameters and returns an Int. Called multiple times, next returns a sequence of values separated by a step amount provided to the primary constructor. Stepper should also provide a primary constructor that accepts a Int argument and sets the step amount.

Stepper maintains two pieces of private state: the step amount, and the current value, which always starts at 0. The step amount is passed to the constructor, and should be part of your primary constructor declaration. But the current value should be declared separately. Calling next increments the current value by the step amount, but returns the previous value. So, for example:

Note that the internal state should be private.

More Practice

Need more practice? Head over to the practice page.