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 😅