Skip to content
Application Development II
GitLabGitHub

OOP in Kotlin

How does Object Oriented Programming work in Kotlin?
Overview

These notes are adapted from slides by Talib Hussain. There may be some mistakes — let me know if there is something here that doesn’t make sense.

  • “Fields” (from Java) are generally called properties
  • “Methods” (from Java) are called class functions
  • visibility is public by default

We’re about to see one of the main reasons to use Kotlin instead of Java, that is, the ability to “avoid boilerplate code”. This is everywhere in Kotlin, but class definitions are an especially clear example.

The best way to see this is just to look for yourself:

/*
The following is a class called SomeClass.
It has a constructor, a private field, and a public setter/getter
*/
public class SomeClass {
private int someField;
public SomeClass(int someField) {
this.someField = someField;
}
public int getSomeField() {
return someField;
}
public void setSomeField(int someField) {
this.someField = someField;
}
}
/*
The following is a class called SomeClass.
It has a constructor, a private field, and a public setter/getter
*/
public class SomeClass {
private int someField;
public SomeClass(int someField) {
this.someField = someField;
}
public int getSomeField() {
return someField;
}
public void setSomeField(int someField) {
this.someField = someField;
}
}

In the following sections, we’ll deconstruct how this is achieved in Kotlin.

For a simple class, we can just define a constructor. Unlike Java, we don’t have to explicitly define all the fields, etc.

Kotlin
class Car(var brand: String, var model: String, var year: Int)
Kotlin
class Car(var brand: String, var model: String, var year: Int)

Constructor arguments can have default values:

Kotlin
class Car(var brand: String,
var model: String = "Unknown",
var year:Int = 2022)
Kotlin
class Car(var brand: String,
var model: String = "Unknown",
var year:Int = 2022)

When calling a constructor in Kotlin, we don’t use the ‘new’ keyword:

Kotlin
val c1 = Car("Ford", "Mustang", 1969)
Kotlin
val c1 = Car("Ford", "Mustang", 1969)

Multiple constructors are possible (self-study).

Kotlin
class Car(var brand: String, var model: String, var year: Int) {
// Class function
fun drive() {
println("Wrooom!")
}
// Class function with parameters
fun speed(maxSpeed: Int) {
println("Max speed is: " + maxSpeed)
}
}
Kotlin
class Car(var brand: String, var model: String, var year: Int) {
// Class function
fun drive() {
println("Wrooom!")
}
// Class function with parameters
fun speed(maxSpeed: Int) {
println("Max speed is: " + maxSpeed)
}
}

Unlike java the getter and setter for a property of a class simply uses the name of the property; i.e. not getX() setX() etc..

Kotlin
val car = Car("Honda", "Camry", 2023)
println(car.brand) // Access with .<property name>
car.model = "Civic" // Set with .<property name>
println(civis.name)
Kotlin
val car = Car("Honda", "Camry", 2023)
println(car.brand) // Access with .<property name>
car.model = "Civic" // Set with .<property name>
println(civis.name)

Kotlin does not have the static keyword.

If you want to create the equivalent to a static method in Kotlin, you can use companion objects.

Companion objects are the singleton objects whose properties and functions are tied to a class but not to the instance of that class. Hence, we can access them just like a static method of the class.

Note that only one companion class is allowed per class. More than one companion object per class will lead to a runtime error in Kotlin.

Kotlin
class MyClass {
companion object{
fun myStaticMethod(): String{
return "This method can be called without object"
}
}
}
fun main(args: Array<String>) {
println(MyClass.myStaticMethod())
}
Kotlin
class MyClass {
companion object{
fun myStaticMethod(): String{
return "This method can be called without object"
}
}
}
fun main(args: Array<String>) {
println(MyClass.myStaticMethod())
}

More information: Kotlin: static member fields and singletons

To allow a class to be subclassed, it must be defined with the keyword open. A class extends another using the colon syntax below:

Kotlin
// Superclass
open class MyParentClass {
val x = 5
}
// Subclass
class MyChildClass: MyParentClass() {
fun myFunction() {
println(x) // x is now inherited from the superclass
}
}
Kotlin
// Superclass
open class MyParentClass {
val x = 5
}
// Subclass
class MyChildClass: MyParentClass() {
fun myFunction() {
println(x) // x is now inherited from the superclass
}
}

If the parent has constructor values, must also pass the appropriate constructor values when subclassing.

Kotlin
// Superclass
open class MyClass(var value:Int)
// Subclass
class SubClass(var value:Int, var otherValue:String): MyClass(value)
Kotlin
// Superclass
open class MyClass(var value:Int)
// Subclass
class SubClass(var value:Int, var otherValue:String): MyClass(value)

A function of a parent class cannot be overridden in a subclass unless the function in the parent is declared open. The override keyword must also be used when defining the overridden function.

Kotlin
open class MyClass(var value:String) {
open fun myFunction() {
println(value)
}
}
class SubClass(var value:String, var otherValue:String): MyClass(value) {
override fun myFunction() {
println(otherValue);
}
}
Kotlin
open class MyClass(var value:String) {
open fun myFunction() {
println(value)
}
}
class SubClass(var value:String, var otherValue:String): MyClass(value) {
override fun myFunction() {
println(otherValue);
}
}

Elsewhere:

Kotlin
val x = MyClass("Hi")
val y = SubClass("Hi", "Bye")
x.myFunction()
y.myFunction()
Should print out "Hi" then "Bye"
Kotlin
val x = MyClass("Hi")
val y = SubClass("Hi", "Bye")
x.myFunction()
y.myFunction()
Should print out "Hi" then "Bye"

Kotlin offers different types of classes, e.g.,

  • data classes
  • enum classes
  • sealed classes Data classes are a very useful concise way to define a class whose purpose is to store data.
  • E.g., Similar to a “POJO” – plain old Java object or JavaBean https://antonioleiva.com/data-classes-kotlin/

Kotlin is designed as a null safe language. By default, variables in Kotlin cannot be set to null.

Instead, there is a special nullable type which allows you to use null subject to certain rules.

Kotlin
var str: String = "xyz"
str = null // Compile-time error
Kotlin
var str: String = "xyz"
str = null // Compile-time error

vs.

Kotlin
var str: String? = "xyz"
str = null // OK
Kotlin
var str: String? = "xyz"
str = null // OK

Kotlin
fun getLength(str: String?): Int? {
return str.length // Compile-time error
}
Kotlin
fun getLength(str: String?): Int? {
return str.length // Compile-time error
}

Since str could be null, checking its length opens you up to the possibility of a null pointer error. Instead:

Kotlin
fun getLength(str: String): Int? {
return str.length // OK
}
Kotlin
fun getLength(str: String): Int? {
return str.length // OK
}

We can also explicitly check for null (“old-school” way)

Kotlin
fun getLength(str: String?): Int? {
if (str != null) {
return str.length // OK
}
return 0
}
Kotlin
fun getLength(str: String?): Int? {
if (str != null) {
return str.length // OK
}
return 0
}

Or can use the safe call operator ?.

Kotlin
fun getLength(str: String?): Int? {
return str?.length // OK
}
Kotlin
fun getLength(str: String?): Int? {
return str?.length // OK
}

This returns null if the given str string is null

public ZipCode getZipCode(User user) {
if (user != null) {
if (user.getAddress() != null)
return user.getAddress().getZipCode();
}
return null;
}
}
public ZipCode getZipCode(User user) {
if (user != null) {
if (user.getAddress() != null)
return user.getAddress().getZipCode();
}
return null;
}
}

  • Recommended: The Google Developer Kotlin Bootcamp is highly recommended for following along with the course so far. At this point, you should know enough to complete all the way to the end of Lesson 4.