Request: remove annotation "@JvmStatic" for "companion object" functions, and treat them as such in Java

#1

Currently, when we convert a Java code into Kotlin, it pollutes all Java files that use the static functions.
This means that if I have this in Java:

public class Foo {

    public static void foo() {
        Log.d("AppLog", "Hello");
    }

    public void foo2() {
        Log.d("AppLog", "World");
    }
}

public class Foo2 {

    public void foo() {
        Foo.foo();
    }
}

And I convert the file of class “Foo”, I get this instead:

class Foo {

fun foo2() {
    Log.d("AppLog", "World")
}

companion object {

    fun foo() {
        Log.d("AppLog", "Hello")
    }
}

}

public class Foo2 {

    public void foo() {
        Foo.Companion.foo();
    }
}

So it changed the Java class file too, and it would have changed all other usages of this static function to be with Companion. , polluting them and causing un-needed clutter.

On the other hand, we can add @JvmStatic to the function on Kotlin, to avoid this and not force the Java code to change.
However, this requires that we add it to every static function, and by definition, it shouldn’t even be needed, because behind the scenes, even though Java “thinks” it’s a static function, it’s not:

Note that, even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects, and can, for example, implement interfaces:

Source: https://kotlinlang.org/docs/reference/object-declarations.html

So my request:
Remove all need for @JvmStatic in the first place.
Make it work the same for all languages. Let Java access it like on Kotlin, even if it looks like it’s something else.

This removes clutter of both @JvmStatic in Kotlin files and Companion. in Java files.
It also doesn’t force us to change the code of both Kotlin and Java.
No effort, and doesn’t cost anything.

#2

Hi. @JvmStatic adds a static method declaration in the bytecode in addition to a non-static one. Without it, you can’t write Foo.foo() in Java, that’s not how Java works. So to drop @JvmStatic we would have to unconditionally generate an additional static method with the same name for every Kotlin method, which is obviously a bad idea.

Anyway, if you feel that @JvmStatic should still be dropped, please submit a feature request to http://kotl.in/issue.

See also: https://youtrack.jetbrains.com/issue/KT-21342

#3

It can be added automatically when it detects a usage of Java in a way of “Foo.foo()”.
It doesn’t need this annotation.
Currently, when we convert the Java file into Kotlin, it doesn’t add this annotation at all, and so converting a single Java files also affects all other Java files that use this class, by adding “INSTANCE” to each of them, spamming them instead of just putting a single annotation per each function that is being used.
This means the annotation is not really needed.

#4

Unfortunately the way Kotlin and Java are compiled do not work in a way that static wrappers can be created on demand. The underpinning cause is the fact that files are compiled individually (and incrementally) mainly for reasons of compilation speed and modularisation (library jars). Another issue is that the Java compiler does not even know about Kotlin so would not be able to recognise the need to generate a forwarding function (or calling the object function directly).

What I think you actually want is that the functions are generated as static on the class file rather than as members of the companion object. This also cannot really work well (as you expect it) due to the semantics of companion objects. Member functions of companion objects (even if they do not refer to the companion object itself - so could be static) do still have the scope to access other members of the companion. This capability is provided at jvm level, not compiler level. To make “static” members of companion objects defined on the outer class means that the companion object itself would need to contain synthetic accessor methods as Java for example generates to access private fields in inner classes (we just reversed the problem but didn’t solve anything).

Still, this path leads us to the compiler determining that a method could be defined as static (in the same way some quick fixes/static analysis can suggest you to make something static). This would technically not be hard but would be linguistically poor. Basically the accessibility of a function from static context would now depend on implementation details rather than be an explicit part of the interface (by providing it a static keyword). The only case were this may be valid is as optimization in the case of private static members. However, it is a very easy optimization that the JVM can do itself, so JVM targeting compilers normally do not optimize this case. Of course private static members are not that interesting to your case either.

#5

Guys you go too far. Please keep it simple.
If I can add this annotation (and similar ones) manually to each place Java files use the functions, the IDE/compiler (or whatever that converts the Java file into Kotlin) can do it on its own too.
It doesn’t matter what Kotlin/Java think between themselves, because the way it works stay the same. The code behind, as written in the docs, stay the same.

The request is to avoid having so many changes to the various Java files, just because we converted a single file from Java to Kotlin.
The changes should be only in the file that was converted. Not in files that use it.

#6

I don’t think anyone here would dislike the idea of improving the jave2kotlin transpiler to add the @JvmStatic annotation to all companion object functions, that were created from static functions.
Just create a feature request (http://kotl.in/issue).
This wasn’t what you argued for in your original post though. You argued to remove the annotation and I think it was explained why this won’t work without creating more problematic downsides.

1 Like
#7

I suggested it before, and I was told that I can request to just remove the annotation, because both result in the same thing anyway.
Please just remove the annotation, or automatically add it to Kotlin.
The purpose is to minimize what is going on other files that use the class. I think they shouldn’t be affected, not by more/less code, and not in how it works behind the scenes.
I want a conversion that doesn’t affect other classes.

#8

I don’t know who suggested that you ask to remove the annotation entirely. I haven’t seen anything like this in the kotlin slack or this forum and I can’t imagine that someone from the kotlin team would have done so.
Both Alexy and pdvrieze explained, why it’s not possible to simply remove the annotation and I suggested an alternative that as far as I can tell would solve your problem. If changing the java2kotlin converter to automatically add the annotation would help you, you simply need to create an issue (and maybe link it here for other people with a similar problem to find it). If you see a problem in my suggestion I love to hear it so we can find a solution.

1 Like
#9

I don’t remember where. Maybe here:
https://youtrack.jetbrains.com/issue/KT-16182

It was told that we should be given a choice of whether to auto-add the annotations or not.
It was also suggested that we would have a LINT check to remove unused or un-needed such annotations (when there is no Java code that uses them).
And someone said that adding the annotations is a clutter, but I explained that having them once per function is less clutter than having “INSTANCE” or something similar in every call to it, and having multiple files being affected is more clutter.
And removing the annotation completely would mean even less clutter. There is really no need for this annotation. Just handle the kotlin code as if everything is annotated, to make Java completely working with Kotlin, without the useless stuff.

#10

Mmmm, this gives an interesting reason for not adding the annotation automatically. I don’t think I agree though, for the same reason that the kotlin compiler does not complain about the annotation being present if it is never used as a static method. The function could be used (statically) from another jar outside of the project.

I think he meant that the choice to add the annotation to each function should be made by you, not that there is a choice of removing the annotation entirely from the kotlin language.


I think there can be a strong argument for adding the annotation automatically. Not doing so would in many cases break existing libraries.
Let’s say you are developing a library in java and start moving to kotlin. You convert some file to kotlin. In your project Intellij automatically replaces all static calls to calls to the companion object. So all your tests run fine. This will however break other projects using your library.

I’m not sure the clutter of the additional Companion reference is a good enough argument.
If you convert the entire program and not just one or a few files adding the annotation is not what you want.
This might be different if you develop a library and don’t want to change your API (as I explained above).


In the discussion on the issue you mentioned interoperability multiple times. Although I understand your argument, I don’t fully agree. Yes, kotlin is committed to interoperability with java, this does not mean however that kotlin can’t have features that java doesn’t or that kotlin needs to support all features java does in a similar way. In other words kotlin only guarantees that every kotlin function can be called from java and vice versa and that they make it as seamless as possible.
For normal functions and classes this means that you won’t even notice them. For properties it means that kotlin automatically generates getters and setters for you and detects java getters and setters and converts them into properties so that you can use them.
There are a few more complicated cases though, mainly inline classes, suspend functions/coroutines and companion objects.
Inline classes for example could be more powerful (eg. with custom constructors) but that would break interoperability so they are not.
For suspend functions there are extra classes in the std-lib so that they can interface with popular java libraries like rx2(https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/).
As for companion objects kotlin decided to go a different route than java and not add static functions to the language. Instead the created the concept of companion objects, which have a few advantages, like the fact that they can use inheritance. Whether this was the right decision is not 100% clear yet (IMO). This means however that the interoperability between java and kotlin on this point is not completely seamless. Kotlin can handle java statics fine, but java code is a bit more verbose in handling companion objects. To make this a bit easier we have the @JvmStatic annotation.

TL;DR I don’t think interop is a good argument to argue against the annotation.

#11

Well I just want to avoid having those annotations as they are not really needed, and I also don’t want to affect so many files in the process.
Having no static functions on Kotlin is a disadvantage. And having a single companion object block is annoying (on most languages constants are supposed to be at the top of the code, and this forces us to put them together with other stuff at the bottom).
About interoperability , I think that if Kotlin is supposed to replace Java, it has to provide the same power of coding as on Java. It’s still weird to me that there is no package-visibility-modifier for functions, as I used it a lot, and because of this the IDE gives me a lot of suggestions for auto-completion that I don’t need.
Can you please give an example in code about your argument, of why not to remove the @JvmStatic and @JvmField ?
For @JvmStatic, is it because the Java class could have both static and normal functions (meaning methods) ?

#12

Removing the annotations from the language, and making companion functions static by default at this point will break compatibility with existing Kotlin code. So the discussion is moot really.

#13

OK .
Can you please make the conversion better though, so that it will auto-add the annotations when needed, or at least give us the choice (and remember it) to do so?