Hi all, I wanted to know what is the meaning of th...
# getting-started
b
Hi all, I wanted to know what is the meaning of the statement "If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s)." in simple English.
k
If your class has a primary constructor, then any secondary constructors that you add need to eventually call it
5
They can either do so by delegating to it directly, or through another secondary constructor that delegates to it. But eventually it needs to reach the primary constructor
5
b
You mean the compiler right?
k
It's a compilation requirement, yeah
r
Here's an example:
Copy code
class Thing(val name: String, val count: Int) {

    // Call primary constructor directly
    constructor(name: String): this(name, 1) {
        println("Using default count")
    }

    // Call primary constructor indirectly
    // through another secondary constructor
    constructor(): this("N/A") {
        println("Using default name")
    }

    // INVALID: Doesn't call primary constructor
    constructor(a: Int, b: Int) {
        println("Nope")
    }

    // INVALID: Delegated constructor never calls primary
    constructor(a: Int): this(a, 0) {
        println("Nope")
    }
}
b
Copy code
class Playground(val name: String, val count: Int) {

    // Call primary constructor directly
    constructor(name: String) : this(name, 1) {
        println("Using default count")
    }

    // Call primary constructor indirectly
    // through another secondary constructor
    constructor() : this("N/A", 2) {
        println("Using default name")
    }
}
I was playing around with this, and I came across this situation when i tried calling the primary constructor directly from both of my secondary constructors, and the result was "Using default name", why didn't the first constructor run?
a
I’m confused as it does what I’d expect:
Copy code
val a = Playground() // prints "Using default name"
val b = Playground("Single Parameter Constructor") // prints "Using default count"
val c = Playground("Calls Primary Constructor", 23) // prints nothing as it's the primary
I’m not sure what you mean “why didn’t the first
constructor
run”? It runs if you pass a single string parameter. You can have the “second”
constructor
call the “first”, which in turn calls the primary:
Copy code
class Playground(val name: String, val count: Int) {

  // Call primary constructor directly
  constructor(name: String) : this(name, 1) {
    println("Using default count")
  }

  // Call primary constructor indirectly
  // through another secondary constructor
  constructor() : this("N/A") { // calls the "first"
    println("Using default name")
  }
}
Side note: I’m not a fan of overloading constructors. I try to look for ways to default the parameter values in the primary whenever possible. This example can be reduced to:
Copy code
class Playground(val name: String = "N/A", val count: Int = 1)
r
@Blitzer Because you're calling
this("N/A", 2)
which calls the primary constructor (with a signature of
Thing(String, Int)
). I wrote
this("N/A")
which calls the other secondary constructor (with a signature of
Thing(String)
). You definitely don't want all constructors to run every time, that would completely violate the purpose of having multiple constructors. If you think about your code, what do you expect the value of
count
to be if both constructors run,
1
or
2
? It can't assign one then the other as
count
is final, so which one wins? And if one wins, why run the other at all?
@Alan B Agreed, what I wrote was not even remotely idiomatic, but the goal was a simple, understandable example. You should definitely use default arguments when you can, but there are situations where overloaded constructors are the right call (though they are indeed rather rare in Kotlin thanks to not only default arguments but also top level functions that can use the same name).
a
@Ruckus My design approach has changed much from switching to Kotlin years ago. Defaulting args, builders, factory methods/functions or implementing
invoke
on a companion object while keeping the constructors private. I often forget how to write a constructor as I just avoid the spaghetti code of the combinatorics of creating instances via constructor args and having to figure out the best way to provide proper hidden defaults. Complex constructors are a code smell (to me). I also avoid subclasses like the plague. That’s a different topic altogether. 😄