Search code examples
kotlingoogle-codelab

how to use lambdas in kotlin


Consider the following code

fun main() {
    val amanda = Person("Amanda", 33, null, null)
    val atiqah = Person("Atiqah", 28, "climb", amanda)

    amanda.showProfile()
    atiqah.showProfile()
}


class Person(val name: String, val age: Int, val hobby: String?, val referrer: Person?) {
    fun showProfile() {
        val getReferrerName: (Person?) -> String = {
            if (it != null) "Has a referrer named ${it.name} who likes to ${it.hobby} ."
            else "Doesn't have a referrer."
        }
        val getHobby: (String?) -> String = {
            if (it != null) "likes to $it"
            else "."
        }
        println("Name: ${name}\nAge: $age\n ${getHobby(hobby)} ${getReferrerName(referrer)}")
    }
}

It gives the following output:

Name: Amanda
Age: 33
 . Doesn't have a referrer.
Name: Atiqah
Age: 28
 likes to climb Has a referrer named Amanda who likes to null .

Instead of showing "null" as hobby of the referrer, I want it to show "."

My Specific Question

I understand that the main issue is in getReferrerName lambda, specifically at " who likes to ${it.hobby} ". Why can't I pass getHobby inside of it, i.e " who likes to ${getHobby}", since it already handles the null case?

P.S I know there are simpler ways to do this with if/else statements, since I'm learning lambdas, I wish to solve it with it.

Output if referrer's hobby is not null:

Name: Amanda
Age: 33
 likes to run Doesn't have a referrer.
Name: Atiqah
Age: 28
 likes to climb Has a referrer named Amanda who likes to run .

Also, I have implemented a temporary solution by implementing "if" logic once again inside the getReferrerName Lambda like so:

val getReferrerName: (Person?) -> String = { 
    if (it != null) "Has a referrer named ${it.name} ${if(it.hobby !=null) "who likes to ${it.hobby}" else ' '} ." 
    else "Doesn't have a referrer." 
}

Solution

  • why can't i pass getHobby inside it i.e " who likes to ${getHobby}", since it already handles null case?

    Well you can write a call to getHobby - just declare getHobby first. Then you can write ${getHobby(it.hobby)}.

    val getHobby:(String?) -> String = { if (it != null)"likes to $it" else "." }
    val getReffererName: (Person?) -> String = { if (it != null) "Has a referrer named ${it.name} who likes to ${getHobby(it.hobby)} ." else "Doesn't have a referrer." }
    

    but it won't produce the desired output. When hobby is not null, it would produce two occurrences of "likes to", e.g.

    likes to climb Has a referrer named Amanda who likes to likes to run .
    

    This would also produce two . characters when hobby is null, which if I understand correctly, is undesirable.

    At the end of the day, the logic of getHobby is simply not very reusable, because you want the hobby to be formatted in one way for the person's own hobby (. when null, and "likes to" prefix), but in another way for the referrer's hobby (empty string when null, and "who likes to" prefix). All that's in common is the "likes to" part. If you want to reuse that, you can do:

    private val hobbyDescription get() = hobby?.let { "likes to $it" }
    private val referrerDescription get() = referrer?.let {
        val hobbyDescription = it.hobbyDescription?.let { " who $it" } ?: ""
        "Has a referrer named ${it.name}$hobbyDescription."
    }
    
    fun showProfile() {
        println("Name: ${name}\nAge: $age\n ${hobbyDescription ?: "."} ${referrerDescription ?: "Doesn't have a referrer."}" )
    }
    

    Note that I made the descriptions of the hobby and referrer private properties instead of local vals of a function type because this makes more sense to me. If you prefer your own style, you can do something like this:

    fun showProfile() {
        val getHobby:(String?) -> String? = {
            if (it != null) "likes to $it" else null
        }
        val getReferrerName: (Person?) -> String = {
            val hobbyDescription = getHobby(it?.hobby)?.let { " who $it" } ?: ""
            if (it != null) "Has a referrer named ${it.name}$hobbyDescription."
            else "Doesn't have a referrer."
        }
        println("Name: ${name}\nAge: $age\n ${getHobby(hobby)} ${getReferrerName(referrer)}" )
    }