Static Methods and Companion Objects in Kotlin
Last Updated :
08 Jun, 2022
Unlike Java, Kotlin doesn't support static methods for a class. Most readers will know that static methods do not belong to the object instance but rather to the type itself. In Kotlin, it is advisable to define methods at the package level to achieve the functionality of static methods. Let's define a new Kotlin file and name it Static. Within this file, we will place the code for a function that will return the first character of the input string (if the input is empty, an exception will be raised), which is as follows:
Kotlin
fun showFirstCharacter(input:String):Char{
if(input.isEmpty()) throw IllegalArgumentException()
return input.first()
}
Then, in your code, you can simply call showFirstCharacter("Kotlin is cool!"). The compiler is here to do some of the work for you. Using javap, we can take a look at the byte code generated. Just run javap -c StaticKt.class to get the code produced by the compiler:
Compiled from "Static.kt"
public final class com.gfg.kotlin.StaticKt {
public static final char showFirstCharacter(java.lang.String);
Code:
0: aload_0
1: ldc #9 //String input
3: invokestatic #15 //Method
kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
...
40: aload_0
41: checkcast #17 //class java/lang/CharSequence
44: invokestatic #35 //Method
kotlin/text/StringsKt.first:(Ljava/lang/CharSequence;)C
47: ireturn
}
As you can see from the printout, the compiler has actually generated a class for us and has marked it as final; it can't be inherited, as you already know. Within this class, the compiler has added the function we defined. Let’s call this method from the program entry point and again using the utility javap we can look at what the bytecode looks like:
fun main(args: Array<String>) {
println("First letter:" + showFirstCharacter("Kotlin is cool")
}
Compiled from "gfg.kt"
public final class com.gfg.kotlin.gfgkt {
public static final void main(java.lang.String[]);
Code:
0: aload_0
...
18: ldc #29 //String Kotlin is cool
20: invokestatic #35 //Method
com/gfg/kotlin/StaticKt.showFirstCharacter:(Ljava/lang/String;)C
}
Most of the bytecode has been left out for the sake of simplicity, but at line 20 you can see there is a call to our method; in particular, the call is made via the invokestatic routine.
We can't talk about static methods and not bring singletons into the discussion. A singleton is a design pattern that limits the instantiation of a given class to one instance. Once created, it will live throughout the span of your program. Kotlin borrows the approach found in Scala. Here is how you can define a singleton in Kotlin:
Kotlin
object Singleton{
private var count = 0
fun doSomething():Unit {
println("Calling a doSomething (${++count} call/-s in total)")
}
}
From any function, you can now call Singleton.doSomething, and each time, you will see the counter increasing. If you were to look at the bytecode produced, you will find out the compiler is doing some of the work for us once again:
public final class com.gfg.kotlin.Singleton {
public static final com.gfg.kotlin.Singleton INSTANCE;
public final void doSomething();
Code:
0: new #10 // class java/lang/StringBuilder
43: return
...
static {};
Code:
0: new #2 //class
com/gfg/kotlin/Singleton
3: invokespecial #61 //Method "<init>":()V
6: return
}
We have left out the code produced for our doSomething method since it is not the focus of this topic. The compiler once again has created a class and marked it final. Furthermore, it has introduced a member called INSTANCE and has marked it static. The interesting part is at the end of the listing where you see the static{}; entry. This is the class initializer, and it is called only once, JVM will make sure this happens, before:
- An instance of the class is created
- A static method of the class is invoked
- A static field of the class is assigned
- A non-constant static field is used
- An assert statement lexically nested within the class is executed for a top-level class
In this case, the code is called before the first call to doSomething because we access the static member INSTANCE (see the following getstatic bytecode routine). If we were to call this method twice, we would get the following bytecode:
public static final void main(java.lang.String[]);
Code:
0: aload_0
1: ldc #9 // String args
3: invokestatic #15 //Method
kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
6: getstatic #21 //Field
com/gfg/kotlin/Singleton.INSTANCE:Lcom/gfg/kotlin/Singleton;
9: invokevirtual #25 //Method
com/gfg/kotlin/Singleton.doSomething:()V
12: getstatic #21 //Field
com/gfg/kotlin/Singleton.INSTANCE:Lcom/gfg/kotlin/Singleton;
15: invokevirtual #25 //Method
com/gfg/kotlin/Singleton.doSomething:()V
18: return
You can see that on both occasions doSomething is called a virtual method. The reason is you can create a singleton that inherits from a given class, as in the example here:
Kotlin
open class SingletonParent(var x:Int){
fun something():Unit{
println("X=$x")
}
}
object SingletonDerive:SingletonParent(10){}
Â
Â
There is a way to call a static method as you would do in Java. To achieve this, you will have to place your object within a class and mark it as a companion object. This concept of a companion object will be familiar to someone with at least entry-level knowledge of Scala. The following example uses the factory design pattern to construct an instance of Student:Â
Â
Kotlin
interface StudentFactory {
fun create(name: String): Student
}
class Student private constructor(val name: String) {
companion object : StudentFactory {
override fun create(name: String): Student {
return Student(name)
}
}
}
As you can see, the constructor for the Student type has been marked private. Thus, it can't be invoked from anywhere apart from inside the Student class or the companion object. The companion class has full visibility for all the methods and members of the Student. From the code, you will need to call Student.create("Jack Wallace") to create a new instance of Student. If you look in the build output, you will notice there are two classes generated for Student: one is Student.class and the other is Student$Companion.class. Let's see how the call to Student.create gets translated into bytecode:
public final class com.gfg.kotlin.gfgkt {
public static final void main(java.lang.String[]);
Code:
0: aload_0
1: ldc #9 //String args
3: invokestatic #15 //Method
kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
6: getstatic #21 // Field
com/gfg/kotlin/Student.Companion:Lcom/gfg/kotlin/Student$Companion;
9: ldc #23 //String Jack Wallace
11: invokevirtual #29 //Method
com/gfg/kotlin/Student$Companion.create:(Ljava/lang/String;)Lcom/gfg/kotlin/Student;
14: pop
15: return
}
In line 6, you will notice there is a call for a static member getstatic. As you can probably imagine, there is a static field added to the Student class of the type Student.Companion:
public final class com.gfg.kotlin.Student {
public static final com.gfg.kotlin.Student$CompanionCompanion;
public final java.lang.String getName();
static {};
Code:
0: new #39 //class
com/gfg/kotlin/Student$Companion
3: dup
4: aconst_null
5: invokespecial #42 //Method
com/gfg/kotlin/Student$Companion."<init>":(Lkotl
in/jvm/internal/DefaultConstructorMarker;)V
8: putstatic #44 //Field
Companion:Lcom/gfg/kotlin/Student$Companion;
11: return
public
com.gfg.kotlin.Student(java.lang.String,kotlin.jvm.internal.DefaultConstructorMarker);
Code:
0: aload_0
1: aload_1
2: invokespecial #24 //Method "<init>":(Ljava/lang/String;)V
5: return
This code snippet proves the assumption is correct. You can see the Companion member being added to our class. And yet again, the class gets class initializer code generated to create an instance of our companion class. Student.create is shorthand for writing code such as Student.Companion.create(). If you were trying to create an instance of Student.Companion (that is, val c = Student.Companion), you would get a compilation error. A companion object follows all the inheritance rules.Â
Similar Reads
Kotlin Class and Objects In Kotlin, classes and objects are used to represent objects in the real world. A class is a blueprint for creating objects (a particular data structure), providing initial values for state (member variables or fields), and implementations of behavior (member functions or methods). An object is an i
4 min read
How to Type Check an Object in Kotlin? Kotlin is a statically typed, general-purpose programming language developed by JetBrains, that has built world-class IDEs like IntelliJ IDEA, PhpStorm, Appcode, etc. It was first introduced by JetBrains in 2011 and is a new language for the JVM. Kotlin is an object-oriented language, and a âbetter
3 min read
Retrofit with Kotlin Coroutine in Android Retrofit is a type-safe http client which is used to retrieve, update and delete the data from web services. Nowadays retrofit library is popular among the developers to use the API key. The Kotlin team defines coroutines as âlightweight threadsâ. They are sort of tasks that the actual threads can e
3 min read
Code Formatting in Kotlin using ktlint As we all know about the kotlin language which is recommended by google particularly for android app development, and of course, it made the life of android app developers easier. But if you are a beginner in this field then you may not know that you need to write the codes in the desired format. an
4 min read
What is Flow in Kotlin and How to use it in Android Project? To build an app asynchronously we have to use RxJava and it is also one of the most important topics in Android Development. But now in Kotlin Jetbrains came up with Flow API. With Flow in Kotlin now we can handle a stream of data sequentially. Flow is a stream processing API in Kotlin developed by
4 min read