Classes in Kotlin

The journey in Kotlin Wonderland continues with an article about classes and objects. Until now we discovered details about Kotlin philosophy, basic types, control flow expressions, null safety and functions.

📌Class Hierarchy

Unit:

Unit in Kotlin corresponds to the void in Java. Like void, Unit is the return type of any function that does not return any meaningful value, and it is optional to mention the Unit as the return type. But unlike void, Unit is a real class (Singleton) with only one instance.

Nothing:

Nothing is a type in Kotlin that represents “a value that never exists”, which means “no value at all”.

Nothing can be used as the return type of a function that never returns the code execution — like, looped forever or always throws Exception. Don’t get confused because Java does not have anything similar to the Nothing type.


fun reportError(): Nothing {
throw RuntimeException()
}
fun displayHelloMessage(): Unit {
println("Hello from Kotlin! 🙂")
}

view raw

Classes.kt

hosted with ❤ by GitHub

📌Classes and Objects in Kotlin

  • Classes and objects in Kotlin work the same way as in most object-oriented languages: a class is a blueprint, and an object is an instance of a class.
  • There are primary and secondary constructors. For secondary we should add the keyword constructor
  • The primary constructor cannot contain any code.
  • Initialization code can be placed in initialization blocks, which are prefixed with the init keyword. The initialization blocks are executed in the same order as they appear in the class body.
  • In Kotlin we have properties. Every property in Kotlin has its own backing field, which contains a property value that can be accessed with the special keyword field
    • property = field + accessor(s)
    • read-only property = field + getter (val)
    • mutable property = field + getter + setter (var)


// primary constructor with fullName property (setter & getter)
class Person(val fullName: String) { /**/ }
// define a secondary constructor
class Person(val fullName: String) {
val age: Int
get() {
return 18
}
// secondary constructor
constructor(fullName: String, age: Int) : this(fullName) {}
}
// instance of the class
val john = Person("John", 24)
// init block
class Person(val fullName: String) {
var isLongName = false
init {
isLongName = fullName.length > 15
}
}

view raw

Classes.kt

hosted with ❤ by GitHub

📌Visibility modifiers

📌Inheritence

  • Inheritance: use open keyword for class
  • Overriding methods and properties: use the open modifier


open class Person {
open val name = "Tom"
open fun displaySkills() { }
}
// inheritance and override
class Student : Person() {
override val name = "Jerry"
override fun displaySkills(){ }
}

view raw

Classes.kt

hosted with ❤ by GitHub

In Kotlin “object” keyword can be used to create singleton objects.


object TheObject {
fun hello() = "hello"
override fun toString() = "Hello, it's me, ${TheObject::class.simpleName}"
}
fun useSingletonObject() {
println(TheObject.hello()) // => hello
val someRef: Any = TheObject
println(someRef) // => Hello, it's me, TheObject
}

view raw

Classes.kt

hosted with ❤ by GitHub

📌Abstract class

  • An abstract class cannot be instantiated.
  • We can override a non-abstract open member with an abstract one
  • Abstract class or abstract function does not need to annotate with open keyword as they are open by default.


abstract class Car {
abstract fun run()
open fun computeTaxes() {}
}
abstract class SafeCar: Car() {
override fun run() {
println("SafeCar is running safely..")
}
override abstract fun computeTaxes()
}

view raw

Classes.kt

hosted with ❤ by GitHub

📌Interface

  • An interface can have both abstract and non-abstract functions.
  • An interface can only have abstract properties (data members)
  • A class can implement more than one interface.
  • All abstract properties and abstract functions of an interface must be overridden in the classes that implement it.


interface Pet {
fun eat()
fun sleep()
}
class Cat : Pet {
override fun eat() {
println("Cat eats fish")
}
override fun sleep() {
println("Cat sleeps a lot")
}
}

view raw

Classes.kt

hosted with ❤ by GitHub

📌Delegation

  • Composition over Inheritance design pattern
  • Native support for delegation (implicit delegation)
  • Zero boilerplate code


interface PetAction {
fun eat()
}
interface PetColor {
val color: String
}
object YellowColor : PetColor {
override val color = "yellow"
}
class PrintingPetAction(val food: String) : PetAction {
override fun eat() {
println(food)
}
}
class Cat(petColor: PetColor = YellowColor) :
PetAction by PrintingPetAction("eats a lot of fish"),
PetColor by petColor
fun delegate() {
val kittyCat = Cat()
println("Pet has color ${kittyCat.color}")
kittyCat.eat()
}
fun main(args: Array<String>) {
delegate()
}
// => Pet has color yellow
// => eats a lot of fish

view raw

Classes.kt

hosted with ❤ by GitHub

📌Data classes

  • Data classes are a concise way to create classes that just hold data. 
  • A data class can’t be abstract, open, sealed or inner.
  • The equals(), hashCode() and toString()methods are automatically generated.
  • Kotlin makes working with immutable data objects easier by automatically generating a copy() function for all the data classes. 
  • Explicit implementations for componentN()and copy() functions are not allowed.
  • The data keyword provides functions that allow destructuring declarations. In short, it creates a function for every property.
  • A data class cannot extend from another data class but it may extend other classes.
  • A data class can’t be abstract, open, sealed or inner.

dataclass


data class Character(val name: String, val age: Int)
fun main() {
val mickeyMouse = Character("Mickey Mouse", 82)
val mickeyMouseToday = mickeyMouse.copy(age = 83)
// destructuring declarations
val (name, age) = mickeyMouseToday
println("$name, $age years of age")
mickeyMouseToday.component1() // => name
mickeyMouseToday.component2() // => age
}

view raw

Classes.kt

hosted with ❤ by GitHub

📌Companion object

  • companion object: syntactically it’s similar to the static methods in Java
  • Often we need only one singleton for a class, and using its full name may seem wordy. For example, we might need to store only one common property. In this case, we can use another Kotlin feature, the companion object.
  • A companion object is a singleton attached to the outer class so it cannot be accessed without accessing the outer class.
  • Only one companion object is available for each class. 
  • We cannot create a companion object inside another singleton (and companion object, too).


class Person {
companion object {
fun callMe() = "Call"
}
}
// Person.callMe()

view raw

Classes.kt

hosted with ❤ by GitHub

📌Enum classes

  • Enum constants aren’t just mere collections of constants – these have properties, methods etc
  • Each of the enum constants acts as separate instances of the class and separated by commas.
  • Enums increases readability of your code by assigning predefined names to constants.
  • An instance of enum class cannot be created using constructors.


enum class Color(val value: Int) {
GOLD(0xffd323),
SILVER(0xeaeaea),
WHITE(0xffffff),
BLACK(0x000000),
RED(0xFF0000)
}
fun printProperty() = println(Color.GOLD.value) // => 16765731
fun printName() = println(Color.GOLD.name) // => GOLD
fun printPosition() = println(Color.GOLD.ordinal) // => 0

view raw

Classes.kt

hosted with ❤ by GitHub

📌Sealed classes

  • A sealed class defines a set of subclasses within it.
  • A sealed class is abstract by itself, it cannot be instantiated directly and can have abstract members.
  • Sealed classes are not allowed to have non-private constructors (their constructors are private by default).
  • Note that classes which extend subclasses of a sealed class (indirect inheritors) can be placed anywhere, not necessarily in the same file.


sealed class Fruit(val name: String) {
class Apple : Fruit("Apple")
class Mango : Fruit("Mango")
}
class Pomegranate : Fruit("Pomegranate")
fun display(fruit: Fruit) {
when (fruit) {
is Fruit.Apple -> println("${fruit.name} is good for iron")
is Fruit.Mango -> println("${fruit.name} is delicious")
is Pomegranate -> println("${fruit.name} contains vit d")
}
}

view raw

SealedClass.kt

hosted with ❤ by GitHub

Check here my previous articles about Kotlin:

Enjoy and feel free to leave a comment if something is not clear or if you have questions. And if you like it please share !

Thank you for reading! 🙌🙏😍✌

Follow me on:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s