Sometimes we need to create an object of some class with slight modification, without explicitly declaring a new subclass for it. Java handles this case with anonymous inner classes. Kotlin uses object expression to achieve the same functionality.
We can even create an object expression for an interface
or abstract
class by just implementing their abstract methods. This functionality is called an anonymous interface implementation or anonymous abstract class implementation.
Let’s understand with examples
We have a class AClass
having someFunction
in it. We want to pass AClass
object to achieve some other functionality but at the same time, we want to override
the default functionality of someFunction
. Ideally, this can be done by creating another class extending AClass
and overriding the someFunction
method and then pass the object of extending class. But we can achieve the same using an anonymous inner class in Java.
//Java code
public class AClass {
public void someFunction(){
System.out.println("SomeClass : some Function");
}
}
public class UsageClass {
public void useSomeClass(AClass someClassObj){
someClassObj.someFunction();
}
}
Now to use the method in UsageClass
,
//Java code
UsageClass usageClass = new UsageClass();
usageClass.useSomeClass(new AClass(){
@Override
public void someFunction() {
super.someFunction();
System.out.println("UsageClass : overridden some Function");
}
});
We created an object of AClass
and passed it to useSomeClass
method also we overridden the functionality of somefunction
without extending the class.
The same can be achieved in Kotlin using object
expression.
//Kotlin code
open class AClass {
open fun someFunction() {
println("SomeClass : some Function")
}
}
class UsageClass {
fun useSomeClass(someClassObj: AClass) {
someClassObj.someFunction()
}
}
Now to use the method in UsageClass
,
//Kotlin code
val usageClass = UsageClass()
usageClass.useSomeClass(object : AClass() {
override fun someFunction() {
super.someFunction()
println("UsageClass : overridden some Function")
}
})
Same can be possible in the case of anonymous interface
implementation or anonymous abstract
class implementation also.
Let’s have a look.
//Java code
public abstract class AAbstractClass {
abstract void doSomething();
public void iAmDoingSomething() {
System.out.println("AAbstractClass : I am doing something");
}
}
public interface IInterface {
void canIDoSomething();
}
public class UsageClass {
public void useAbstractClass(AAbstractClass abstractClassObj) {
abstractClassObj.doSomething();
abstractClassObj.iAmDoingSomething();
}
public void useInterface(IInterface iInterfaceObject) {
iInterfaceObject.canIDoSomething();
}
}
Now to use the functions in the UsageClass
,
//Java code
UsageClass usageClass = new UsageClass();
usageClass.useAbstractClass(new AAbstractClass() {
@Override
void doSomething() {
System.out.println("UsageClass : I am doing something");
}
});
usageClass.useInterface(new IInterface() {
@Override
public void canIDoSomething() {
System.out.println("UsageClass : can I Do Something");
}
});
The equivalent Kotlin code for the same using object
expression will be,
//Kotlin code
abstract class AAbstractClass {
internal abstract fun doSomething()
fun iAmDoingSomething() {
println("AAbstractClass : I am doing something")
}
}
interface IInterface {
fun canIDoSomething()
}
class UsageClass {
fun useAbstractClass(abstractClassObj: AAbstractClass) {
abstractClassObj.doSomething()
abstractClassObj.iAmDoingSomething()
}
fun useInterface(iInterfaceObject: IInterface) {
iInterfaceObject.canIDoSomething()
}
fun useSomeClass(someClassObj: AClass) {
someClassObj.someFunction()
}
}
To use the UsageClass
,
//Kotlin code
val usageClass = UsageClass()
usageClass.useAbstractClass(object : AAbstractClass() {
override fun doSomething() {
println("UsageClass : I am doing something")
}
})
usageClass.useInterface(object : IInterface {
override fun canIDoSomething() {
println("UsageClass : can I Do Something")
}
})
If a supertype has a constructor, appropriate constructor parameters must be passed to it. Many supertypes may be specified as a comma-separated list after the colon :
//Kotlin code
open class BClass(val x: Int){
open val y = 20
}
interface IInterface {
fun canIDoSomething()
}
val bClassObj : BClass = object : BClass(10), IInterface {
override val y: Int = 30
override fun canIDoSomething() {
println("bClassObj : can I do something")
}
}
We can even create just an object with no nontrivial supertypes. This is called an anonymous object.
//Kotlin code
val justAnObject = object {
var x = 10
var y = 20
fun sum(): Int = x + y
}
But the anonymous objects can only be used as types only in local
and private
declarations. If you use an anonymous object as a return type of a public function or the type of a public property, the actual type of that function or property will be the declared supertype of the anonymous object, or Any
if you didn’t declare any supertype. In that case, the members added in the anonymous object will not be accessible.
//Kotlin code
class CClass {
// Private function, so the return type is
// the anonymous object type
private fun privateFunction() = object {
val x: String = "x"
}
// Public function, so the return type is Any
fun publicFunction() = object {
val x: String = "x"
}
fun main() {
val x1 = privateFunction().x // Works fine
val x2 = publicFunction().x // ERROR:
//Unresolved reference 'x'
}
}
One last thing, unlike Java we can access non-final variables also inside an object
expression.
//Java code
int value = 0;
usageClass.useInterface(new IInterface() {
@Override
public void canIDoSomething() {
value++; // Error
// variable 'value' is access from withing inner class, needs to be declared final.
}
});
In Kotlin, this works fine
//Kotlin code
var value = 0
usageClass.useInterface(object : IInterface {
override fun canIDoSomething() {
value++ // works fine
}
})