<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=160269078105920&amp;ev=PageView&amp;noscript=1">
Sign up for our blog, talk to a specialist, or just send us an email

Annotations for your Java-friendly Kotlin code

Tuesday 03 of July, 2018./ Reading Time: 14 minutes./ By Miguel Aybar Guerrero

 You are here because:

  1. You have finally decided to try Kotlin.
  2. You loved it, because, why wouldn't you?
  3. You want to use it everywhere.
  4. You face the sad reality: you can't not get rid of Java, at least not without some effort.

Why?

If Kotlin is awesome, why can't I just use it all the time? Two common scenarios come to my mind:

  1. When you're trying to slowly migrate your codebase to Kotlin, you'll notice that sometimes there are some files you just don't dare Convert Java to Kotlin fileIf you have enough time to refactor it, then go for it! In a real project though, that may not always be the case.
  2. Your code is going to be consumed by both Java and Kotlin programmers. You can't (or shouldn't) force all of them to use one specific language, especially if supporting both will take you little to no effort (using annotations of course).

Here you'll find a few annotations that will give you Java & Kotlin interoperability!

Java Annotations

JvmField
  • What does it do? Instructs the Kotlin compiler not to generate getters/setters for this property and expose it as a field.
  • Most common use case: To expose fields from a companion object.
How does it work?

Let’s say you define a field inside an object / companion object in Kotlin:

object Constants {

val PERMISSIONS = listOf("Internet", "Location")

}

 

If you try to invoke that function from Java, you would have to write:

Utils.INSTANCE.getPERMISSIONS()

 

That's a lot of code for a simple field! To make the code cleaner, let’s get rid of it by adding the annotation.

object Constants {

@JvmField

val PERMISSIONS = listOf("Internet", "Location")

}

 

Now, our Java code will look like this:

Utils.PERMISSIONS;

 

We can achieve the same by using the modifier instead of

However, this modifier can only be applied to primitive types or Strings.

//Kotin

object Constants {

const val KEY = "test"

}



//Java

String key = Constant.KEY;

When can’t I use it?

Properties const, marked as and functions, cannot be annotated with @JvmField

JvmStatic
  • What does it do? If used on a function, it specifies that an additional static method needs to be generated from this element. If used on a property, additional static getter/setter methods will be generated.
  • Most common use case: To expose members (functions, properties) from a companion object.
How does it work?

JvmField is very similar to as you will see.

Let’s say you define a function inside an object in Kotlin:

object Utils {

fun doSomething(){ ... }

}

 

If you try to invoke that function from Java, you would have to write:

Utils.INSTANCE.doSomething()

 

We’ll have to access the object INSTANCE whenever we want to invoke that function. To make the code cleaner, let’s get rid of it by adding the @JvmStatic annotation.

object Utils {

@JvmStatic

fun doSomething(){ ... }

}

 

Now, when invoking that function from Java, all we need to do is:

Utils.doSomething();

 

That’s much better, isn’t it? It’s just as if the function was originally written as a static method in Java.

We can also apply that annotation to fields:

object Utils {

@JvmStatic

var values = listOf("Test 1", "Test 2")

}

 

So when calling it from Java, we can put:

Utils.getValues();

 

Notice that JvmField exposes the member as a field, but with JvmStatic we expose a get function.

And because our field is a var, the set method is also generated:


Utils.setValues(...);

 

If we have a constant inside our object, we can also declare it as static:


object Utils {

@JvmStatic

val KEY = "test"

}

 

Yet, in this case, using this annotation wouldn’t be the best idea, because the invocation call would look like this:


public void foo(){

String key = Utils.getKEY();

}

 

For that scenario, use the const modifier or JvmField as explained before.

When can't I use it?

A member can't be annotated with JvmField when it has an open, override, or const modifier.

In this situation, the code won’t compile:

image40

JvmOverloads
  • What does it do? Instructs the Kotlin compiler to generate overloads for this function that substitutes default parameter values.
  • What is an“overload”? In Kotlin, your function may have default parameters, and that allows you to invoke the same function in multiple ways. To achieve that in Java, you would have to manually define every single variation of that function. Each of those automatically generated variations is an “overload”.
  • Most common use case: To overload class constructors. Yet, it can be applied to any kind of function that has default parameters.
How does it work?

If you have a class with a constructor (or any other function) with default parameters…


class User constructor (

val name: String = "Test",

val lastName: String = "Testy", val age: Int = 0

)

 

 … you would be able to invoke it from Kotlin in multiple ways:


val user1 = User()

val user2 = User(name = "Bruno")

val user3 = User(name = "Bruno", lastName = "Aybar")

val user4 = User(name = "Bruno", lastName = "Aybar", age = 21)

val user5 = User(lastName = "Aybar")

val user6 = User(lastName = "Aybar", age = 21) val user7 = User(age = 21)

val user8 = User(age = 21, name = "Bruno")

...

 

However, if you try to invoke the constructor from Java, you would only have two options: 1) to pass all the parameters, or 2) only in the case ALL your parameters have a default value, you could pass no parameters at all.

image16

If we want to create overloads, we can use the JvmOverloads annotation:


class User @JvmOverloads constructor ( val name: String = "Test",

val lastName: String = "Testy", val age: Int = 0

)

 

Now, multiple options are available when using Java:

image48

However, here we don’t have as many variations as in Kotlin. For example, there is no way to only pass a last name or age.

The JvmOverloads annotation will only generate as many overloads as default parameters in the function.

  • When can’t I use it? There are few limitations for this annotation. As long as you have a function, you can mark it as JvmOverload. You can even combine it with other annotations like JvmStatic.
  • When shouldn't I use it? This annotation is useless if your function has no default parameters.
@file:JvmName
  • What does it do? Specifies the name for the Java class or method which is generated from this element.
  • Most common use case: to give a nicer name to a Kotlin file that only have functions. However, it can be applied not only to files but also to functions and property accessors (getters and setters).
How does it work?

In Kotlin, where functions are first-class citizens, you can write functions that exist outside of a class. For instance, if you create a new Kotlin file and write this code, it will compile with no problems:


//file name: Utils.kt



fun doSomething() { ... }

 

You can invoke that code from Java:


UtilsKt.doSomething();

 

Notice that even though the file name is Utils, the invocation name is UtilsKt, which is not ideal. To fix this, let's add the JvmName annotation at the top of the file.


//file name: Utils.kt

@file:JvmName("Utils")



fun doSomething() { ... }

 

Notice that we are using the file: prefix. As you may have guessed, it specifies that the annotation we are using applies at the file-level. If you invoke that code from Java:


Utils.doSomething();

 

We can also annotate functions:


//file name: Utils.kt @file:JvmName("Utils")




@JvmName("doSomethingElse")

fun doSomething() { ... }

 

When calling it from Kotlin, we will still use the original name (doSomething), but in Java we use the name specified on the annotation:


//Java Utils.doSomethingElse();


//Kotlin Utils.doSomething()

 

This may not seem useful, but it can be used to handle signature clashes. That scenario is wonderfully explained in the official documentation.

It also works with properties accessors:


class User {

val likesKotlin: Boolean = true

@JvmName("likesKotlin") get() = field

}

 

So here is how the Java invocation would look like with, and without, the annotation:

//Without  annotation

new User().getLikesKotlin()



//With  annotation

new User().likesKotlin()

 

The same can be achieved using the get prefix.


class User {

@get:JvmName("likesKotlin")

val likesKotlin = true

}

 

  • When can I use it? You can use it on files, function, and property accesors, but remember to use the correct prefixes when needed.
  • When shouldn’t I use it? Randomly assigning an alternate name to a function can cause a lot of confusion. Use it cautiously and keep it coherent.

I hope this guide to Annotations for Java-friendly Kotlin code was helpful for you. The nice thing about Kotlin is that you don’t have to choose only the one language, but can make it accessible to all.

About Avantica

If you are looking for a software partner who will work towards your own business goals and success, then Avantica is your solution. We offer dedicated teams, team augmentation, and individual projects to our clients, and are constantly looking for the best methodologies in order to give you the best results.

 

Let’s start a project together

Join our newsletter

 

 

 

PREVIOUS
AI Hype- Why is Everyone Talking About AI?
NEXT
Data Mining on Azure ML Studio