Object Expressions
Frequently, we need to pass an instance of an object to some function, where we need a tailored instance of the desired class, with custom functionality.
In Java, this is where the “anonymous inner class” comes into play:
void clickyClickyClicky(View view) {
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO something useful
}
});
}
This sort of code will be familiar to long-time Android developers. To find out when this UI element (a View
) gets clicked, we can call setOnClickListener()
, providing an implementation of a OnClickListener
interface. Rather than create a standalone class, though, we use anonymous inner class syntax to create a class and a single instance of that class.
Kotlin’s equivalent of that sort of syntax is the “object expression”.
Basic Declaration
The Kotlin equivalent of the View
and OnClickListener
from above might look like this:
interface OnClickListener {
fun onClick(v: View)
}
class View {
fun setOnClickListener(listener: OnClickListener) {
// TODO something with this
}
}
(technically, in Android, OnClickListener
is a nested interface inside of View
— we will explore that pattern in an upcoming chapter)
We have the View
class and an OnClickListener
interface, along with a setOnClickListener()
function.
When we want to call setOnClickListener()
, we could create a standalone class that implements the OnClickListener
interface. Or, we could use an object expression instead:
fun clickyClickyClicky(view: View) {
view.setOnClickListener(object : OnClickListener {
override fun onClick(v: View) {
// TODO something useful
}
})
}
The object
keyword indicates that we are creating a single object, and the : OnClickListener
indicates that this object will implement the OnClickListener
interface. From there, it is no different than any other interface implementation: we override
the onClick()
function and do whatever it is that we want to do.
Constructors and Parameters
Object expressions can extend other classes and implement interfaces. The notation is pretty much the same as when you have a class extend other classes and implement interfaces. The biggest difference is that you use the object
keyword in place of the class name.
In the above example, OnClickListener
is an interface. As a result, our object expression could just have the interface name after the colon (: OnClickListener
) to implement it, because interfaces have no constructors. Classes do, so if OnClickListener
were an abstract class, you would need to invoke a constructor in your object expression:
abstract class OnClickListener {
abstract fun onClick(v: View)
}
class View {
fun setOnClickListener(listener: OnClickListener) {
// TODO something with this
}
}
fun clickyClickyClicky(view: View) {
view.setOnClickListener(object : OnClickListener() {
override fun onClick(v: View) {
// TODO something useful
}
})
}
Access Rules
Your object expression has access to anything that is available to other Kotlin code in the same scope. So, for example, if there is a local variable in a function where the object expression is declared, the local variable is accessible by the object expression’s own code:
fun clickyClickyClicky(view: View) {
var clicks = 0
view.setOnClickListener(object : OnClickListener() {
override fun onClick(v: View) {
clicks++
println(clicks)
}
})
}
In Java, this requires the final
keyword to work, and you would not be allowed to modify the value the way we are doing here. Kotlin is more flexible: you do not need any keywords and you have normal access to the variable.
The SAM Scenario
On occasion, you will find Kotlin code that seems to be akin to an object expression but just has a lambda for its implementation:
val listener = OnClickListener { v: View -> ... }
(where ...
is some useful Kotlin code)
This is the single-abstract-method pattern. We will examine it in greater detail later in the book, because as it turns out, this only works if the interface being used (OnClickListener
) is defined in Java, not in Kotlin.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.