Demystifying Advanced Kotlin Concepts Pt.2-1

praveenkajla

Praveen Kajla

Posted on October 14, 2017

Demystifying Advanced Kotlin Concepts Pt.2-1

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
            }
        }
}

Enter fullscreen mode Exit fullscreen mode

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)

}

Enter fullscreen mode Exit fullscreen mode

If we run this :

output -> 
  0.0
  200.0
  200.0

Enter fullscreen mode Exit fullscreen mode

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

    ....

Enter fullscreen mode Exit fullscreen mode

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()
    }
}

Enter fullscreen mode Exit fullscreen mode

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()
    }
}

Enter fullscreen mode Exit fullscreen mode

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.

    ...

Enter fullscreen mode Exit fullscreen mode

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


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

Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

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)
    }
}

Enter fullscreen mode Exit fullscreen mode

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()
}


Enter fullscreen mode Exit fullscreen mode

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"
}

Enter fullscreen mode Exit fullscreen mode

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() 

...
Enter fullscreen mode Exit fullscreen mode

Next: Classes Pt.2

💖 💪 🙅 🚩
praveenkajla
Praveen Kajla

Posted on October 14, 2017

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related