Function overload ambiguity

Hi,

I have two functions in DefaultTest as below.

class Main {
    fun main(args: Array<String>) {
        val a = DefaultTest()
        a.test("1", "1")
    }
}

class DefaultTest {
    fun test(a: String, b: String?) {
        println("test1")
    }

    fun test(a: String, b: String, c: String = "c") {
        println("test2")
    }
}

When I run the class, the result is “test2”. Actually, I was expected “test1”.
So, I run this code after changing b: String? to b: String of the first test function.
The result was “test1”.

I was wondering that how compiler pick a function to run.
I think it might be overload ambiguity.

2 Likes

I couldn’t find anything in the language specification that describes the behaviour you see. It’s probably “undefined behavior”, unfortunately.

1 Like

It works like this for the same reason why the most specific function is chosen in the following example:

fun test(s: String) = print("test1")
fun test(s: Any) = print("test2")
1 Like

You should use:

fun main(args: Array<String>) {
    val a = DefaultTest()
    a.test("1", "1" as String?)
}
2 Likes

@fatjoe79 Thank you for the reply. I think it has not the same reason.
It depends on nullability and parameter (defined in Kotlin compiler) not the different type like String and Any.

Is there any way to run String? parameter function?

fun test(s: String) = print("test1")
fun test(s: Any) = print("test2")

val string : String? = "test"
a.test(string) //type mismatch error

Also, below code doesn’t compile by that the same JVM signuare test (Ljava/lang/String;)V

fun test(s: String) = print("test1")
fun test(s: String?) = print("test2")

@fvasco Thank you for reply.

The result goes to “test2” with explicit casting String?.
It’s ok in Kotlin perspective. But it’s not in Java perspective.
In Java perspective, there is no need to cast to run fun test(a: String, b: String?).

//Java
DefaultTest a = new DefaultTest();
a.test("1","1"); //"test1"
//Kotlin
val a = DefaultTest()
a.test("1", "1" as String?) //"test1"
a.test("1", "1") //"test2"

So, I’m looking for language specification about that. Is this “undefined behavior” or not?
For better Java Interoperability and safety, it might be overload ambiguity or something like warning.
It’s possibility to run unexpected behavior by the overload functions.

In Kotlin String is a more specific type than String?.
In Java does the statement String.valueOf("hello") invoke String.valueOf(String) or String.valueOf(Object)? It invoke the more specific type.

In your Java experiments you can replace Object for String?.

1 Like

I thought String is the same type as String? except nullability.
So, I was wondering how Kotlin compiler resolves function overloading by what kind of criteria.
For this case, Kotlin compiler picks a more specific type method. (“test2”)

Thank you all.

I also would like more documentation about overload resolution. Say we have

fun test(a: Any) = TODO()
fun test(a: String, b: String = "Hello") = TODO()
fun test(vararg a: String) = TODO()

does calling test("Hello") call the first method with the right number of parameters, but a less specific type, the second version with the right type but the unneeded second parameter, or the third version with a varargs parameter and the right type?

Sure, you could just try it, but it should be formally documented somewhere. I haven’t found such documentation, yet.

1 Like

I think that two of the predetermined speakers have right at least regarding missing explanation in documentation.

In my humble developer opinion (not as language creator) this is inconsistent behaviour, which stays against what Kotlin’s team is officially presenting in their “Kotlin Type System Hierarchy”, where difference between null and not null types is explicitly marked.

It’s inconsistent because if want to run

class MyClass

fun notNull(name: MyClass){}

main(){
   val myClass : MyClass ?= null
   notNull(myClass)
}

compailer will complain Type mismatch inferred type is MyClass? but MyClass was expected

Of course MyClass is not a String. But in other hand they both ihnerit from Any, which should mean that they both behave the same