5

I know it's not "strictly by the design pattern" blah blah blah, but...

In Kotlin, is there a way to create a "default-ish" setter that returns "this", like

var foo:Bar = Something()
    set(f:Bar) {
       foo = f
       return this // Alas, that would not compile because Setter returns Unit
    }

It is very convenient when a setter return this, because can then make a Builder pattern without having to declare a Builder. It's just shorter to do:

BlahBlah().setFoo(x).setFoo2(y)...

Than

BlahBlah.Builder().setFoo(x)....

or

var b = BlahBlah()
b.setFoo(x)
b.setFoo2(y)
...

Or whatever

And besides, if a setter returns Unit anyway, why not this just as well?

Meymann
  • 2,520
  • 2
  • 28
  • 22

3 Answers3

10

Kotlin has nice documentation on how to create type safe builders. The setters in Kotlin are invoked as

receiver.property = value

Returning this from the setter method would only help in case the code is used from Java. Having said that a Kotlin setter must return Unit. Even without a specialized builder a typical task of setting several properties on an object is much more concise in Kotlin:

class BlahBlah {
  var name = "John"
  var age = 12
}

BlahBlah().apply {
  name = "Sarah"
  age = 10
}
miensol
  • 39,733
  • 7
  • 116
  • 112
  • 1
    ...And, the great part, everything in the apply block is received by "this". You can even call methods! val coolText=TextView(context).apply{text="moo"; textSize=TSIZE; animate(myCoolAnimation);} To sum it all up: Why using a 1950's VW when you got a brand new Maserati? apply is way more powerful than old Java builder-ish setters. – Meymann Jul 06 '17 at 13:48
3

Please take a look at How to implement Builder pattern in Kotlin?

There isn't a built-in mechanism but you you can leverage apply to write self-returning methods easily:

class Foo {
    var bar: Bar
    fun bar(b: Bar) = apply { bar = b }
}
Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
  • Nice convention. As long as you don't forget to use it as a method, not as property access. But liked it. – Meymann Jul 06 '17 at 09:22
2

No, that is not possible: Kotlin's setters return Unit by design.

The Builder pattern exists to overcome the problem where the increase of constructor parameter combinations results in an exponential list of constructors. In Kotlin this problem is solved by having default parameter values:

data class Foo(
  val a1: String = "a1",
  val a2: String = "a2"
)

Instead of using the Builder pattern, you can now easily skip parameters by naming them:

val foo = Foo(a1 = "bar")

If you really want to have some sort of Builder:

class FooBuilder {
    var a1: String = "a1"
    var a2: String = "a2"

    fun build() = Foo(a1, a2)
}

val foo = FooBuilder().apply {
  a1 = "bar"
}.build()

However, this requires a lot more code.

nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • The problem is mostly from Java. Old frustrating Java... Because I got tons of code I wanna slowly migrate. Kotlin did that beautifully. It's just the old Java that's annoying. – Meymann May 29 '17 at 09:26