kotlinkotlin-java-interopkotlin-stdlib

Use a top-level val with a function type as if it were a static function from Java


A top-level val with a function type can be used like a "static" function from Kotlin, but is there a way to use it like a static function from Java as well?


For example, say we have a file called Example.kt that contains absolutely nothing besides this

val foo: (String) -> String = { s -> "Hello, $s" }

Using this in other Kotlin code works as you would expect in any functional language

foo("world")

Conceptually, this definition of foo is no different than the following Java code:

public class ExampleKt {
  public static String foo(String s) { 
    return "Hello, " + s;
  }
}

However, if you actually have to call it from Java, it's pretty janky:

ExampleKt.getFoo().invoke("world")

Desired behavior would be similar to how it can be used in Kotlin, like

import static ExampleKt.foo;

foo("world");

In various other parts of the language, we can use @JvmStatic to make calling "static" kotlin code less janky from Java. Is there a way to do it here?


Solution

  • Although Kotlin generally does a great job at masking what's going on behind the scenes, a lambda is not the same as a function in the JVM bytecode. It's true that conceptually, there's not many differences because Kotlin really leans into the whole "functions as first-class citizens" thing, but that's mostly just syntactic sugar.

    For example, compare the bytecode output of an actual top-level function: https://godbolt.org/z/T7KK11ev7

    and the bytecode of a top-level lambda val: https://godbolt.org/z/WP1j6WzzT

    TL;DR the lambda val gets compiled to a Functor class (ExampleKt$test$1) which implements kotlin.jvm.functions.Function0<kotlin.Unit>. An instance of that class gets stored in a private static field of the ExampleKt class, with a corresponding static getter.

    The compilation output is very reminiscent of how Java handles lambdas: They are essentially classes implementing a functional interface (like Function, Consumer, Supplier, ...), and everything else is syntactic sugar. It's just that Kotlin goes one step further and you can use function types instead of functional interfaces.

    So I guess the answer to your question is no, there's no way to make this lambda seem like a static function in Java, unless you actually declare it as a fun. And @JvmStatic can't help here since your val foo already gets compiled to a static member, but it's a field and not a function.