Kotlin: static, which is not

    This article will discuss the use of statics in Kotlin.
    Let's start.
    There is no static in Kotlin!

    This is stated in the official documentation.

    And it seems that this could have been the end of the article. But excuse me, how is it? After all, if you insert Java code into a file on Kotlin in Android Studio, then a smart converter will make magic, turn everything into code in the required language and everything will work! But what about full compatibility with Java?

    In this place, any developer, learning about the lack of static in Kotlin, will get into the documentation and forums to deal with this issue. Let's sort it out together, thoughtfully and painstakingly. I will try to keep as few questions as possible on this topic by the end of this article.

    What is the static behavior in Java? There are:
    • static class fields
    • class static methods
    • static nested classes

    Let's do an experiment (this is the first thing that comes to mind).

    Create a simple Java class:
       publicstatic String staticField = "Hello, static!";
       publicstaticvoidsetStaticValue(String value){
           staticField = value;

    Everything is easy here: in the class we create a static field and a static method. We make everything public for experiments with access from the outside. We associate the field and method logically.

    Now we will create an empty Kotlin-class and try to copy into it all the contents of the SimpleClassJava1 class. We answer the resulting question about conversion with “yes” and see what happened:

       var staticField = "Hello, static!"fun setStaticValue(value: String){
           staticField = value

    It seems that this is not exactly what we need ... To make sure of this, we transform the byte-code of this class into Java-code and see what happened:
      @NotNullprivate String staticField = "Hello, static!";
      @NotNullpublicfinal String getStaticField(){
      publicfinalvoidsetStaticField(@NotNull String var1){
         Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
         this.staticField = var1;
      publicfinalvoidsetStaticValue(@NotNull String value){
         Intrinsics.checkParameterIsNotNull(value, "value");
         this.staticField = value;

    Yes. Everything is exactly as it seemed. No static here and does not smell. The converter simply chopped off the static modifier in the signature, as if it had never existed. Just in case, we immediately conclude: do not blindly trust the converter, sometimes it can give unpleasant surprises.

    By the way, about half a year ago, converting the same Java code into Kotlin would have shown a slightly different result. So again: be careful with automatic conversion!

    Experiment further.

    Go to any class on Kotlin and try to call static elements of a Java class in it:
    SimpleClassJava1.staticField = "hello!!!"

    Here is how! Everything is beautifully called, even autocomplete code tells us everything! Pretty curious.

    We now turn to the more substantive part. Indeed, the creators of Kotlin decided to move away from static in the form in which we used to use it. Why it was done this way and not otherwise we will not argue - there are plenty of disputes and opinions about this on the net. We'll just figure out how to live with it. Naturally, we are not just deprived of statics. Kotlin gives us a set of tools with which we can compensate for the lost. They are suitable for internal use. And the promised full compatibility with Java code. Go!

    The quickest and simplest thing you can realize and start using is the alternative that we are offered instead of static methods, the functions of the package level. What it is? This is a function that does not belong to any class. That is, this kind of logic that is in a vacuum somewhere in the package space. We can describe it in any file inside the package of interest. For example, let's call this file JustFun.kt and place it in the packagecom.example.mytestapplication
    package com.example.mytestapplication
    fun testFun(){
        // some code

    Convert the bytecode of this file to Java and take a look inside:
        // some code

    We see that in Java a class is created whose name takes into account the name of the file in which the function is described, and the function itself becomes a static method.

    Now, if we want to call a function in Kotlin testFunfrom a class (or the same function) located in a package package com.example.mytestapplication(that is, the same package as the function), then we can simply access it. If we call it from another package, then we must import, familiar to us and usually applicable to classes:
    import com.example.pavka.mytestapplication.testFun

    If we talk about calling the function t estFunfrom Java code, then the import of the function must always be done, no matter from which package we call it:
    importstatic com.example.pavka.mytestapplication.ForFunKt.testFun;

    The documentation states that in most cases, instead of static methods, we just need to use the functions of the package level. However, in my personal opinion (which does not have to coincide with the opinion of everyone else), this method of implementing statics is only suitable for small projects.
    It turns out that these functions do not belong explicitly to any class. Visually, their call looks like a call to a class method (or its parent) in which we are, which can sometimes be confusing. Well and the main thing - function with such name can be only one in a package. Even if we try to create a function of the same name in another file, the system will give us an error. If we talk about big projects, then we often have, for example, different factories that have the same static methods.

    Let's look at other alternatives for implementing static methods and fields.

    Recall what a static class field is. This is a class field, belonging to the class in which it is declared, but not belonging to a specific instance of the class, that is, it is created in a single copy for the entire class.

    Kotlin suggests us to use for this purpose some additional entity, which also exists in a single copy. In other words - singleton.

    For declaring singletons, Kotlin has an object keyword.

    object MySingltoneClass {
    // some code

    Such objects are initialized lazily, that is, at the time of the first access to them.

    Ok, in Java, too, there are singltons, and here is static?

    For any class in Kotlin, we can create a companion or companion object. A certain singleton tied to a particular class. This can be done using 2 keywords together companion и object:

    companion object{
       var companionField = "Hello!"fun companionFun(vaue: String){
           // some code

    Here we have a class SimpleClassKotlin1, inside which we declare a singleton using the object keyword and bind it to the object, inside which it is declared the keyword companion. Here you can pay attention to the fact that, unlike the previous singleton declaration (MySingltoneClass), the name of the singleton class is not indicated. In case the object is declared a companion, it is allowed not to indicate its name. Then he will be automatically assigned a name Companion. If needed, we can get a companion class instance in this way:
    val companionInstance = SimpleClassKotlin1.Companion

    However, an appeal to the properties and methods of a companion class can be done directly through the reference of the class to which it is associated:

    This is a lot like calling static fields and classes, isn't it?

    If necessary, we can assign a name to the companion class, but in practice this is done very rarely. Among the interesting features of the accompanying classes, it can be noted that, like any regular class, it can implement interfaces, which can sometimes help us add a bit more order to the code:

    interfaceFactoryInterface<T> {
        fun factoryMethod(): T
    class SimpleClassKotlin1 {
        companion object : FactoryInterface<MyClass> {
            override fun factoryMethod(): MyClass = MyClass()

    A companion class can have only one class. However, no one forbids us to declare any number of singleton objects within a class, but in this case we must explicitly specify the name of this class and, accordingly, specify this name when referring to the fields and method of this class.

    Speaking of classes declared as object, we can say that we can also declare nested objects in them, but we cannot declare a companion object in them.

    It's time to look under the hood. Take our simple class:

       companion object{
           var companionField = "Hello!"fun companionFun(vaue: String){
       object OneMoreObject {
           var value = 1fun function(){

    Now we decompile its bytecode in Java:
      @NotNullprivatestatic String companionField = "Hello!";
      publicstaticfinal SimpleClassKotlin1.Companion Companion = new SimpleClassKotlin1.Companion((DefaultConstructorMarker)null);
         privatestaticint value;
         publicstaticfinal SimpleClassKotlin1.OneMoreObject INSTANCE;
            return value;
         publicfinalvoidsetValue(int var1){
            value = var1;
         static {
            SimpleClassKotlin1.OneMoreObject var0 = new SimpleClassKotlin1.OneMoreObject();
            INSTANCE = var0;
            value = 1;
         @NotNullpublicfinal String getCompanionField(){
            return SimpleClassKotlin1.companionField;
         publicfinalvoidsetCompanionField(@NotNull String var1){
            Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
            SimpleClassKotlin1.companionField = var1;
         publicfinalvoidcompanionFun(@NotNull String vaue){
            Intrinsics.checkParameterIsNotNull(vaue, "vaue");
         // $FF: synthetic methodpublicCompanion(DefaultConstructorMarker $constructor_marker){

    Look what happened.

    The property of a companion object is represented as a static field of our class:
    privatestatic String companionField = "Hello!";

    It seems that this is exactly what we wanted. However, this field is private and is accessed through the getter and setter of our companion class, which is represented here as public static final class, and its instance is represented as a constant:
    publicstaticfinal SimpleClassKotlin1.Companion Companion = new SimpleClassKotlin1.Companion((DefaultConstructorMarker)null);

    The companionFun function did not become a static method of our class (probably it should not). It remained a singleton function, initialized in the SimpleClassKotlin1 class. However, if you think about it, then logically it is about the same thing.

    With the class, the OneMoreObjectsituation is very similar. It is worth noting that here, in contrast to the companion, the field of the value class did not move to the class SimpleClassKotlin1, but remained in OneMoreObject, but also became static and received the generated getter and setter.

    Let's try to comprehend all of the above.
    If we want to implement static fields or class methods in Kotlin, then for this you should use the companion object declared inside this class.
    Calling these fields and functions from Kotlin will look quite similar to calling statics in Java. And what will happen if we try to call these fields and functions in Java?

    Autocomplete tells us that the following calls are available:

    That is, here we will not get anywhere from the direct indication of the name of the partner. Accordingly, it uses the name that was assigned to the default companion object. Not very comfortable, is it?

    However, the creators of Kotlin made it possible to make it look more familiar to Java. And for this there are several ways.
    @JvmFieldvar companionField = "Hello!"

    If we apply this annotation to the field of companionFieldour companion object, then when converting bytecode to Java, we see that the static field companionFieldSimpleClassKotlin1 is not private, but public, and the Companion getter and setter for companionField are missing in the static class . Now we can access from the Java code in the companionFieldusual way.

    The second way is to specify a modifier for properties of the companion object lateinit, properties with late initialization. Do not forget that this applies only to var-properties, and its type must be non-null and must not be primitive. Well, do not forget about the rules for dealing with such properties.

    lateinit var lateinitField: String

    And one more way: we can declare the property of a companion object to be constant, by giving it a const modifier. It is easy to guess that this should be a val-property.
    const val myConstant = "CONSTANT"

    In each of these cases, the generated Java code will contain the public static field we are used to; in the case of const, this field will also be final. Of course, it should be understood that each of the 3 cases has its own logical purpose, and only the first one is designed specifically for ease of use with Java, the rest receive this “bun” as if to a load.

    Separately, it should be noted that the const modifier can be used for properties of objects, companion objects and for package level properties. In the latter case, we get the same as when using the functions of the package level and with the same restrictions. Java code is generated with a static public field in the class, whose name takes into account the name of the file in which we described the constant. There can be only one constant in the package with the specified name.

    If we want the function of the companion object to also be converted to a static method when generating Java code, then for this we need to apply an annotation to this function @JvmStatic.
    It is also permissible to apply the annotation @JvmStaticto the properties of companion objects (and just singleton objects). In this case, the property will not turn into a static field, but a static getter and setter to this property will be generated. For a better understanding, let's take a look at this Kotlin-class:
       companion object{
           @JvmStaticfun companionFun(vaue: String){
           @JvmStaticvar staticField = 1

    In this case, the following references are valid from Java:
    int x;
    x = SimpleClassKotlin1.getStaticField();
    x = SimpleClassKotlin1.Companion.getStaticField();

    From Kotlin such calls are valid:

    It is clear that for Java one should use the first 3, and for Kotlin the first 2. The remaining calls are only valid.

    Now it remains to clarify the latter. How to deal with static nested classes? Everything is simple - an analogue of such a class in Kotlin is the usual nested class without modifiers:

    After converting bytecode to Java, we see:

    Indeed, this is what we need. If we do not want the class to be final, then in the Kotlin code we specify the open modifier to it. I remembered this just in case.

    I think you can summarize. Indeed, in Kotlin itself, as mentioned, there is no static in the form in which we are accustomed to see it. But the proposed toolbox allows us to implement all types of statics in the generated Java code. Full compatibility with Java is also provided, and we can directly call static fields and methods of Java classes from Kotlin.
    In most cases, static implementation in Kotlin requires a few more lines of code. Perhaps this is one of the few, and maybe even the only case when you need to write more to Kotlin. However, you get used to it quickly.
    I think that in projects where Kotlin and Java code are used together, you can be flexible in choosing which language to use. For example, for storing constants, as it seems to me personally, Java is more suitable. But here, as in many other things, one should be guided by both common sense and the rules for writing code in the project.

    And at the end of the article here is another such information. Perhaps, in the future, a static modifier will appear in Kotlin, eliminating many issues and making life easier for developers, and the code is shorter. I made this assumption by finding the corresponding text in clause 17 of the Kotlin feature feature descriptions .
    True, this document dates from May 2017, and the end of 2018 is already outside.

    I have it all. I think that the topic was disassembled in some detail. Write questions in the comments.

    Also popular now: