Matthew Layton
Matthew Layton

Reputation: 42229

Kotlin - constructor default values and constructor overloading

Consider the following code:

class Foo(val bar: String, val baz: Boolean = true) {
    constructor(bar: String) : this(bar, false)
}

Without the addition of a secondary constructor, I could call Foo("") because the second argument has a default value. This would result in baz being true.

With the addition of a secondary constructor, I can still call Foo(""), except that now baz is false.

Why does Kotlin not see this as a duplicate constructor signature, since they can both be called with the same arguments?

Upvotes: 5

Views: 5574

Answers (1)

Willi Mentzel
Willi Mentzel

Reputation: 29844

If you take a look at the bytecode there are actually three constructors generated, as Roland already pointed out.

public Foo(@NotNull String bar, boolean baz) { ... }
public Foo(String var1, boolean var2, int var3, DefaultConstructorMarker var4) { ... }
public Foo(@NotNull String bar) { ... }

So, there are no duplicate constructor signatures. Now one might ask how Kotlin chooses which overload to take judging from the call-site only.

The overall rationale is that the most specific function/constructor will be chosen from the overload candidates.

This is what the Kotlin language specification says about it:

  • For each candidate, we count the number of default parameters not specified in the call (i.e., the number of parameters for which we use the default value);

  • The candidate with the least number of non-specified default parameters is a more specific candidate;


I know that you intended this to only be an example, but if something like this happens in a real-world situation one should avoid it like the Kotlin Language Documentation (page 76) states:

If you have an object with multiple overloaded constructors that don't call different superclass constructors and can't be reduced to a single constructor with default argument values, prefer to replace the overloaded constructors with factory functions.

class Foo2(val bar: String, val baz: Boolean = true) {
    companion object {
        fun factoryCreate(s: String) = Foo2(s, false)
    }
}

In this case it will always be clear right away (without thinking about the overloading resolution rules) what baz will be after creation.

Upvotes: 6

Related Questions