Overload functions in Kotlin vs Java

I wonder why Kotlin does not follow Java way in resolution of overloaded functions ambiguity. Java choses the most specific method at compile time based on types, whereas Kotlin gives compilation error. Please consider examples.

Java:

    public interface Logger {
        void debug(Object entry, Throwable error);
        void debug(Function<Object, Object> entryProducer, Throwable error);
        void debug(String template, Object arg1, Object ... args);
    }

    public void test(Logger log) {
        log.debug("aaa", new IllegalStateException());

        log.debug("aaa", "b");
    }

Kotlin:

    interface Logger  {
        fun debug(entry: Any, error: Throwable?)
        fun debug(entryProducer: () -> Any, error: Throwable?)
        fun debug(template: String, param1: Any?, vararg params: Any?)
    }

    fun test(log: Logger) {
        log.debug("aaa", IllegalStateException())

        log.debug("aaa", "b")
    }

Java compiles and resolves method according to expectation:

Kotlin does not compile

1 Like

Kotlin also uses the same rule. Looking at this specific example, I’m not even sure which method was chosen by the Java compiler and why. None of these methods look more specific to me. Most probably, Java uses different rules in details, e.g. first parameter takes precedence or vararg plays some role here.

4 Likes

Do you actually know which method is being chosen by the Java code? Which method do you think it’s choosing, or expect it to choose? Is it actually choosing the one you expect it to choose?

I agree with broot, the Java code is very ambiguous. I have no idea which function it’s picking. I’m inclined to guess the first one, since the second argument being an instance of Throwable might be more specific than the first argument being a String, but that’s a guess. I’m not actually confident that I know which function is being used. Very confusing code.

3 Likes

I think Java considers methods with varargs always last, which makes sense in cases like

public void add(String s) { ... }
public void add(String ... strings) {
    // probably wants to refer to the other add() 
}

Varargs in Kotlin work different (they don’t have to be the last argument, there is the spread operator, etc), which could explain the difference.

4 Likes

Yes, you are right, method with Throwable was chosen (the first one in Java example).

The important point though is that I wasn’t certain which one was being picked. I had to guess. That’s not a good situation cause it makes your code base more confusing.

1 Like

Well… Obviously Slf4j authors are not concerned with this consideration :slight_smile:

Tbf that one’s more consistent in that the first argument is always a String. It’s not like the original code you posted (unless that’s also taken from SLF4J). I think in that case, it works in Java and probably in Kotlin by doing more specific matching first. If the first argument is a String, and the second argument is a Throwable, it picks the function with the String and Throwable argument. If the first argument is a String, and the second argument is anything that is not a Throwable, then it picks the function with String and Object argument. If your first argument is ever NOT a String, then none of the functions will match.

The problem with the code in your original post is that neither option is more specific. If the first argument is a String, and the second argument is anything that is not a Throwable, then it picks the third debug function, no problems. If the first argument IS a String, and the second argument is a Throwable, that’s when it all goes wrong. Picking the first debug function, the String argument is not an exact match, but the Throwable is. Picking the third debug function, the String argument is an exact match, but the Throwable is not. So which is the correct one?

In the SLF4J code, it’s either both arguments are an exact match, or one argument is an exact match. In your original code, it’s only ever one argument that is an exact match, so which one should be the exact match?

3 Likes