https://kotlinlang.org logo
Title
s

Shawn

06/20/2018, 3:14 PM
maybe this’ll end up being a dumb question, but what’s the right approach to take when you have a property you want to assign conditionally based on which constructor you call? the class I’m writing kinda looks like this
class Foo(val bar, val baz) {
  val doober = mutableMapOf<String, String>()
  val qux: Set<String>
  constructor(bar, baz, lad) : this(bar, baz) {
    // do things to `doober` using `lad`
    qux = doober.keys.toSet()
  }
}
more specifically,
qux
should be an empty set if the primary constructor is called, and a computed, read-only set if the secondary constructor is called, based on the results of evaluating the parameter unique to it. I tried a separate
init
block declared after the secondary constructor to populate the set using the map, but it looks like the init block gets executed before the logic in the body of the secondary constructor. for kicks, I also tried making
qux
a constructor parameter, but you can’t call the primary constructor from the secondary one using values you compute in the latter
e

edwardwongtl

06/20/2018, 3:33 PM
"Note that code in initializer blocks effectively becomes part of the primary constructor. Delegation to the primary constructor happens as the first statement of a secondary constructor, so the code in all initializer blocks is executed before the secondary constructor body. Even if the class has no primary constructor, the delegation still happens implicitly, and the initializer blocks are still executed" Quoted from official website.
s

Shawn

06/20/2018, 3:35 PM
gotcha
e

edwardwongtl

06/20/2018, 3:36 PM
class Foo {
    val doober = mutableMapOf<String, String>()
    val qux: Set<String>

    constructor(bar: String, baz: String) {
        qux = emptySet()
    }

    constructor(bar: String, baz: String, lad: String) {
        // do things to `doober` using `lad`
        qux = doober.keys.toSet()
    }
}
This could work
By making the first
constructor
a non-primary one, they can both initialize
qux
inside the constructor.
s

Shawn

06/20/2018, 3:39 PM
what if
bar
and
baz
are `private val`s?
e

edwardwongtl

06/20/2018, 3:40 PM
class Foo {
    private val bar: String
    private val baz: String
    val doober = mutableMapOf<String, String>()
    val qux: Set<String>

    constructor(bar: String, baz: String) {
        qux = emptySet()
        this.bar = bar
        this.baz = baz
    }

    constructor(bar: String, baz: String, lad: String) {
        // do things to `doober` using `lad`
        qux = doober.keys.toSet()
        this.bar = bar
        this.baz = baz
    }
}
s

Shawn

06/20/2018, 3:40 PM
is that not the same code?
e

edwardwongtl

06/20/2018, 3:41 PM
I pressed enter too early
s

Shawn

06/20/2018, 3:41 PM
ah gotcha
kind of irritating that you have to assign bar and baz twice though
e

edwardwongtl

06/20/2018, 3:43 PM
True, but they are in different constructor, so...
Kind of a trade off atm
Maybe you can file a issue on this and see if JetBrain will take into account
s

Shawn

06/20/2018, 4:42 PM
so after spending way too much time trying to resolve this neatly, I settled on making that last parameter nullable with null as the default parameter, which gets rid of the secondary constructor and uses a
?.let { ... }
to do an assignment
it looks… not amazing, but it’s not the literal worst
I also don’t know if it’s a code smell to have a nullable param that defaults to null
e

edwardwongtl

06/20/2018, 4:48 PM
definitely not
1