If you have a few years of experience with the Kotlin language and server-side development, and you’re interested in sharing that experience with the community, have a look at our **Contribution Guidelines**.

# The @JvmStatic Annotation in Kotlin

Last modified: July 16, 2021

## 1. Overview

In this article, we’re going to see how the *@JvmStatic *annotation affects the generated bytecode. Also, we’ll get familiar with the use cases of this annotation.

Throughout the article, we will take a peek at the bytecode pretty extensively to see what happens under the hood in different cases.

## 2. The *@JvmStatic *Annotation

For the sake of demonstration, we’re going to use a very simple Kotlin file throughout the article. So, let’s create a file named *Math.kt*:

```
class Math {
companion object {
fun abs(x: Int) = if (x < 0) -x else x
}
}
fun main() {
println(Math.abs(-2))
}
```

This file contains a class with a companion object and a *main()* function. The *main* function calls the *abs() *function, which calculates the absolute value of any given number*. *This is, of course, not the best implementation for *abs* but will definitely serve as a good example.

### 2.1. Without Annotation

**For many of us, the companion object in Kotlin is a tool to implement static-like behavior**. So, naturally, we may expect that the Kotlin compiler will compile the *abs() *function as a static method under the hood.

In order to verify this assumption, first, let’s compile the *Math.kt* file:

`>> kotlinc Math.kt`

After compilation, there will be three class files:

```
>> ls *.class
Math$Companion.class
Math.class
MathKt.class
```

As shown above, one class file is for the *main *function, one for the *Math *class, and one for the companion object.

Now, let’s take a peek at the generated JVM bytecode using the *javap *tool:

```
>> javap -c -p Math
public final class Math {
public static final Math$Companion Companion;
public Math();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
static {};
Code:
0: new #13 // class Math$Companion
3: dup
4: aconst_null
5: invokespecial #16 // Method Math$Companion."<init>":(LDefaultConstructorMarker;)V
8: putstatic #20 // Field Companion:LMath$Companion;
11: return
}
>> javap -c -p -v Math
// omitted
InnerClasses:
public static final #17= #13 of #2; // Companion=class Math$Companion of class Math
```

As the bytecode represents, **Kotlin compiles the companion object as a static inner class**. Additionally, it defines a *static final *field to hold an instance of the inner class inside the enclosing one (in this case, the *Math* class):

`public static final Math$Companion Companion;`

In order to initialize this static field, it uses a static initializer block:

```
static {};
Code:
0: new #13 // class Math$Companion
3: dup
4: aconst_null
5: invokespecial #16 // Method Math$Companion."<init>":(LDefaultConstructorMarker;)V
8: putstatic #20 // Field Companion:LMath$Companion;
11: return
```

As shown above, it first creates a new instance of the companion object (index 0), then calls its constructor (index 5), and finally, stores the new object inside that static field (index 8).

Now, let’s check out the bytecode for the companion object:

```
>> javap -c -p Math.Companion
public final class Math$Companion {
// omitted
public final int abs(int);
Code:
0: iload_1
1: ifge 9
4: iload_1
5: ineg
6: goto 10
9: iload_1
10: ireturn
}
```

As we can see here, **Kotlin compiles the abs() function as a simple instance method and not a static one**. So, every time we call this function from another Kotlin function, it will be a simple instance method call:

```
>> javap -c -p MathKt // main function
public final class MathKt {
public static final void main();
Code:
0: getstatic #12 // Field Math.Companion:LMath$Companion;
3: bipush -2
5: invokevirtual #18 // Method Math$Companion.abs:(I)I
// omitted
}
```

Here, we can see that calling the *Math.abs() *method from Kotlin translated to a simple (and not static) method call to *Math.Companion.abs()*.** If we’re going to call this function from Java, we only can use the Math.Companion.abs() approach**:

```
Math.abs(-2) // won't compile
Math.Companion.abs(-2) // works
```

The first one won’t even compile because there’s no static method named *abs() *in the *Math *class. The only way we can access this functionality from Java is through the *Companion *static final field that we saw earlier.

### 2.2. With Annotation

**When we put the @JvmStatic annotation on the abs() function, Kotlin will also generate an additional static method**. For instance, this is when we use the annotation:

```
class Math {
companion object {
@JvmStatic
fun abs(x: Int) = if (x < 0) -x else x
}
}
```

Now, if we compile the above file, we can see that Kotlin actually generates an addition static method for the *abs()* function:

```
>> javap -c -p Math
public final class Math {
public static final Math$Companion Companion;
public static final int abs(int);
Code:
0: getstatic #17 // Field Companion:LMath$Companion;
3: iload_0
4: invokevirtual #21 // Method Math$Companion.abs:(I)I
7: ireturn
// same as before
}
```

Interestingly, **this static method delegates to the Companion.abs() method**. Please note that this static method will be generated in addition to all other methods and classes we saw in the previous section.

**Since there is a static method now, we can call this function from Java both ways**:

```
Math.abs(-2)
Math.Companion.abs(-2)
```

However, **Kotlin will continue to call the instance method instead of the static one**:

```
>> javap -c -p MathKt
public final class MathKt {
public static final void main();
Code:
0: getstatic #12 // Field Math.Companion:LMath$Companion;
3: bipush -2
5: invokevirtual #18 // Method Math$Companion.abs:(I)I
// omitted
}
```

Let’s look at part of the bytecode generated for this Kotlin snippet:

```
fun main() {
println(Math.abs(-2))
}
```

From this, **we can understand that this annotation is there for Java interoperability and won’t be necessary for pure Kotlin codebases**.

Moreover, the* @JvmStatic* annotation can also be applied to a property of an object or a companion object. This way, the corresponding getter and setter methods will be static members in that object or the class containing the companion object.

Just to recap, Kotlin will generate a bytecode corresponding to the following Java code when we use *@JvmStatic*:

```
public class Math {
public static final Companion Companion = new Companion();
// only if we use @JvmStatic
public static int abs(int x) {
return Companion.abs(x); // referring to the static field above
}
public static class Companion {
public int abs(int x) {
if (x < 0) return -x;
return x;
}
private Companion() {} // private constructor
}
}
```

If we omit the *@JvmStatic *annotation, the bytecode would be the same except for the static method.

## 3. Use Cases for *@JvmStatic*

**The most important use case for the @JvmStatic annotation is Java interoperability**. More specifically, with static methods, it’s easier to integrate with some Java-first frameworks. For instance, JUnit 5 requires the method sources to be static:

```
@ParameterizedTest
@MethodSource("sumProvider")
fun `sum should work as expected`(a: Int, b: Int, expected: Int) {
assertThat(a + b).isEqualTo(expected)
}
companion object {
@JvmStatic
fun sumProvider() = Stream.of(
Arguments.of(1, 2, 3),
Arguments.of(5, 10, 15)
)
}
```

In addition to that, calling *@JvmStatic *methods is a bit easier and more idiomatic from the Java world.

## 4. Conclusion

In this article, we saw how the *@JvmStatic *annotation affects the generated JVM bytecode. Put simply, this annotation tells the Kotlin compiler to generate one additional static method for the annotated function under the hood.

Moreover, the most important use case for this annotation is, of course, better Java interoperability. With this method, it’s easier to integrate with some Java frameworks such as JUnit. It’s also easier and more idiomatic to call such methods from Java.

As usual, all the examples are available over on GitHub.