58

given following Kotlin class:

class Foo {
   public fun bar(i: Int = 0): Int = 2 * i
}

How should I call 'bar' function without any parameter from a java/groovy code?

def f = new Foo()
f.bar() //throws:  java.lang.IllegalArgumentException: Parameter specified as non-null contains null
2

2 Answers 2

130

You can do this now in Kotlin. For your class method, use the @JvmOverloads annotation.

class Foo {
    @JvmOverloads public fun bar(name: String = "World"): String = "Hello $name!"
}

Now simply call it from Java:

Foo foo = new Foo();
System.out.println(foo.bar());
System.out.println(foo.bar("Frank"));

Outputs the following:

Hello World!

Hello Frank!

Sign up to request clarification or add additional context in comments.

4 Comments

While using @JvmOverloads, please keep the following in mind, 1. Using default arguments on parent class/interface trickles to child implementations as well. No need to make the params in overridden methods accept default arguments. They by default do so for all Kotlin usage. 2. JvmOverloads can't be used on interface methods. Causes compilation errors. 3. If you override a method which accepts default params and make the params default -- causes compilation error. They already accept default params for Kotlin use. 4. Can't use JvmOverloads on overridden method -- signature conflict.
thank you very much for the things to note for @JvmOverloads @mdsayubi
@MMK yes, it does. You can easily test this with a Kotlin class and Java class in the same project and see what your IDE (assuming IntelliJ IDEA) shows you.
what if you have KDoc for that method? will it duplicate the javadoc across both java methods?
2

I'll post the real answer shortly, but if anyone is wanting to do this from reflection, here is how the code would look. Much more complicated, but educational about how to use Kotlin reflection for KCallable.

Here is the class to call:

 class Foo {
    public fun bar(name: String = "World"): String = "Hello $name!"
}

Then we need a utility class in Kotin that can receive an instance of a class, a method from java reflection, and the parameters by name. This only works with non-primitives:

class KReflectHelper {
    companion object {
        @Suppress("UNCHECKED_CAST")
        @JvmStatic fun <T> callKotlinMethodWithNamedParms(instance: Any, method: Method, parmMap: Map<String, Any>): T {
            val callable: KFunction<T> = method.kotlinFunction as? KFunction<T> ?: throw IllegalStateException("Method is not a Kotlin method")
            val unusedParms = HashSet(parmMap.keys)
            val callableParms = hashMapOf<KParameter, Any?>()
            callable.parameters.map { parm ->
                if (parm.kind == KParameter.Kind.INSTANCE) {
                    callableParms.put(parm, instance)
                } else if (parm.kind == KParameter.Kind.VALUE && parmMap.contains(parm.name)) {
                    unusedParms.remove(parm.name)
                    callableParms.put(parm, parmMap.get(parm.name))
                } else if (parm.kind == KParameter.Kind.VALUE) {
                    if (parm.isOptional) {
                        // default value will be used!
                    } else {
                       throw IllegalStateException("Missing required parameter ${parm.name}")
                    }
                } else {
                    throw IllegalStateException("Cannot call methods that are not direct instance methods")
                }
            }
            if (unusedParms.isNotEmpty()) {
                throw IllegalStateException("Unrecognized parameters passed to function: $unusedParms")
            }
            return method.kotlinFunction?.callBy(callableParms) as T
        }
    }
}

Now that static method can be called from Java, but it isn't so much fun. A code generator would really be required. Calling it from Kotlin is much easier and some frameworks (such as Klutter and Kovert) already use something along these lines.

    Foo foo = new Foo();
    System.out.println(foo.bar("Frank"));

    Method barMethod = Foo.class.getMethod("bar", String.class);

    Map<String, Object> parms = new HashMap<String, Object>();

    parms.put("name", "David");
    System.out.println(KReflectHelper.callKotlinMethodWithNamedParms(foo, barMethod, parms));

    // now call using the default
    parms.clear();
    System.out.println(KReflectHelper.callKotlinMethodWithNamedParms(foo, barMethod, parms));

Ouput:

Hello Frank!

Hello David!

Hello World!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.