loading...

Demystifying Advanced Kotlin Concepts Pt.2-1

praveenkajla profile image Praveen Kajla ・3 min read

This article is a follow-up to Demystifying Advanced Kotlin Concepts Pt.1

Classes in Kotlin


1.Fields

Remember Classes in Kotlin cannot have fields.

However, sometimes it is necessary to have a backing field (A private field that stores the data exposed by a public property)when using custom accessors.
For these purposes, Kotlin provides an automatic backing field which can be accessed using the field identifier:

Let's create a class Customer having mutable property (using the var keyword) lastPurchased.
We also create custom getter and setter for this property.


class Customer(){

    var lastPurchased:Double = 0.0 // Property type is optional as kotlin provide type inference
        get() = field //field is built-in variable and can only be used in getter and setter
        set(value){
            if(value>100){
                field = value
            }
        }
}

Here getter and setter are optional as kotlin provide default getter and setter .

But since we are conditionally setting the value we need our own getter and setter.


fun main(args: Array<String>) {

    val customer = Customer() // no new keyword needed to instantiate
    println(customer.lastPurchased)
    customer.lastPurchased = 200.0
    println(customer.lastPurchased)
    customer.lastPurchased = 50.0
    println(customer.lastPurchased)

}

If we run this :

output -> 
  0.0
  200.0
  200.0

A backing field for a property is automatically created if :

  • a custom getter or setter references it through the field identifier
  • default implementation of at least one of the accessors is used

For cases where field is not enough we can't just use backing field for whatever reasons ,then the only way around it is to create a private property like :


class Customer(){

    private var myCustomField = 10

    ....

2. Late Initialization

Oftentimes we need to have late initialization of a property.

Let's create a controller of web for Customer class above that spits out some data from a repository (database)


interface Repository{
    fun getAll(): List<Customer>
}

class CustomerController(){

    var repository:Repository // ide would show "Property must be initialized or be abstract"

    fun index():String{
        return repository.getAll().toString()
    }
}

Now imagine we want repository to be a property that needs to be initialized by some IoC container i.e. we are not passing this property as a part of constructor but I want it to be initialized later on.

So one solution is to make it nullable i.e.


class CustomerController(){

    var repository:Repository? = null

    fun index():String{
        return repository?.getAll().toString()
    }
}

But then every single time we access repository we need to suffix it with ?.
And also we don't want this to be null or let people reading our code to assume that we want it to be null.

SO kotlin provide modifier lateinit to solve this problem :


class CustomerController(){

    lateinit var repository:Repository // tells the compiler that we are going to initialize it later on.

    ...

However, this is not gonna make everything safe.
If we run without initializing repository


fun main(args: Array<String>) {
    val cc = CustomerController()
    cc.index()
}

We won't get the null reference but :


Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialized
    at CustomerController.index(Class.kt:15)
    at ClassKt.main(Class.kt:21)

Which makes debugging easier.

3. Nested Classes

Much like nested functions or local functions we can have nested classes in kotlin.

Let's create a class DirectoryExplorer having function listFolder which also encapsulates the functionality to check
permission whether a specific user can access that folder.

class DirectoryExplorer(){

    class PermissionCheck(){

        fun validatePermission(user: String) {

        }

    }

    fun listFolder(folder:String,user:String){

        val permissionCheck = PermissionCheck()
        permissionCheck.validatePermission(user)
    }
}

So we have PermissionCheck as the nested class.
Also, we can access this nested class and create an instance of that.


fun main(args: Array<String>) {
    val de = DirectoryExplorer()
    val pc = DirectoryExplorer.PermissionCheck()
}


If you don't want it to be accessed or instantiated make it private.

Now, what if we want to access properties of the external class inside the nested class.
To do that we would have to use modifier inner as the prefix to the nested class.


class DirectoryExplorer(val user:String){

    inner class PermissionCheck(){
        fun validatePermission() {
            if(user != "Bruce"){

            }
        }
    }
}

fun main(args: Array<String>) {
    val de = DirectoryExplorer("Bruce")
    val pc = DirectoryExplorer.PermissionCheck() //Ide would show "Constructor of inner class PermissionCheck can be called only with receiver of containing class"
}

using inner modifier makes the nested class the part of the actual instance of the external class, that's why we can't access inner class simply.

So it would be accessible as a part of the actual instance of the class i.e.

...

    val pc = DirectoryExplorer().PermissionCheck() 

...

Next: Classes Pt.2

Posted on by:

Discussion

markdown guide