Kotlin Object Expression – What more can object do?

In a previous post I explained what Kotlin Object Declarations are. This time around it’s about the declaration’s sibling, the Object Expression.

An object is not just a glorified static replacement or a singleton. object can be used where Java usually utilizes anonymous inner classes. Let’s look at a more realistic scenario: a JButton and an ActionListener or a MouseListener.

Now, ActionListener only has a single method so adding a listener to a button can be done with a lambda.

val guiWidget = JButton()
guiWidget.addActionListener { e -> }

But, if you need an object of some type that requires more customization then, guess what, you use object. And because object can inherit from another type you can create an anonymous object that implements MouseListener. For all intends and purposes it is very much like an anonymous inner class.

guiWidget.addMouseListener(object : MouseListener {
    override fun mouseReleased(e: MouseEvent?) {}
    override fun mouseExited(e: MouseEvent?) {}
    override fun mousePressed(e: MouseEvent?) {}
    override fun mouseClicked(e: MouseEvent) {}
    override fun mouseEntered(e: MouseEvent) {}
})

A Kotlin object does have a nice trick up its sleeve that Java does not. If you just need a localized data container – not in the sense that it can be translated 😉 – then object can help you out too. Like in dynamic languages.

val qna = object {
    val question : String = "The ultimate question of life, the universe and everything"
    val answer = 42
}

This saves a lot of typing since no explicit class declaration is required. Of course, you would use a data class in Kotlin to accomplish that goal so it wouldn’t be much code to write anyway. And as mentioned in my previous post, Kotlin doesn’t even require it to be in its own file.

data class Pair(val question: String, val answer: Int)

For the curious like me, this is what it compiles down to when reversing the class file to Java code.

public final class ArtificialObjectExpressions$anonymousObjects$qna$1 {
   @NotNull
   private final String question = "The ultimate question of life, universe and everything";
   private final int answer = 42;

   @NotNull
   public final String getQuestion() {
      return this.question;
   }

   public final int getAnswer() {
      return this.answer;
   }

   ArtificialObjectExpressions$anonymousObjects$qna$1() {
   }
}

As was to be expected, a separate class file had to be generated by the compiler to hold the two properties of the qna "ad-hoc" object.

Objects can also be return types of functions. There’s a "but" coming, though. The actual type that is returned depends on whether the function is private or not. Only if it is private the type of the object is resolved, i.e. you can access its members. If the function is not private, then your object is of type Any or any other declared supertype of the object. That means you don’t have easy access to the object’s members.

fun publicObjectReturned() = object {
    val question : String = "The ultimate question of life, universe and everything"
    val answer = 42
}

private fun privateObjectReturned() = object {
    val question : String = "The ultimate question of life, universe and everything"
    val answer = 42
}

// Usage examples.

// Not working, because the type is Any in this case.
val publicReturn = publicObjectReturned()
// println("Question ${publicReturn.question}")
// println("Answer ${publicReturn.answer}")

// Working.
val privateReturn = privateObjectReturned()
println("Question ${privateReturn.question}")
println("Answer ${privateReturn.answer}")

There is a limit though. If you want to return an ad-hoc object from a function that is being created somewhere in the function, then you are out of luck.

private fun privateObjectReturned2() : Any {
    val obj = object {
        val question : String = "The ultimate question of life, universe and everything"
        val answer = ArtificialObjectDeclarations.THE_ANSWER_TO_THE_ULTIMATE_QUESTION
    }
    // Do something private useful.
    return obj
}

// Usage example (that is not really an example).

// Not working, because the return type is explicitly Any.
val privateReturn2 = privateObjectReturned2()
// println("Question ${privateReturn2.question}")
// println("Answer ${privateReturn2.answer}")

In that case you need something like a data class so it has a name. This limits the usage of object return types to simple functions that compute a few properties or conveniently group properties into an object. For complex logic that wants to return more than a simple Int a proper class declaration is required.

I’ve borrowed most of my examples from the Kotlin documentation on that topic and only tried to rephrase them to make it easier to understand. For me personally I have succeeded – but I’m hardly objective 😅

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 )

Twitter picture

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

Facebook photo

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

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.