```data class Coordinate(val x: Int, val y: Int) { val bottom: Coordinate = copy(y = y + 1) }``` I...
t
Copy code
data class Coordinate(val x: Int, val y: Int) {
  val bottom: Coordinate = copy(y = y + 1)
}
I am trying to do the following thing, but I get
StackOverflowError
is it because I am trying to refer something that I am defining?
Copy code
data class Coordinate(val x: Int, val y: Int)
val Coordinate.bottom: Coordinate get() = copy(y = y + 1)
this one works though 🤷
r
currently, the
bottom
value is eagerly defined as a property, so creating an instance of
Coordinate
initializes the
bottom
property, which creates an instance of
Coordinate
, which initializes the
bottom
property, ... etc
🙌 1
in the second example, it's defined as a property getter, so the
copy
function only gets called when you get the
bottom
property explicitly
the Java equivalent of the first is:
Copy code
public class Coordinate {
  private Coordinate bottom = this.copy(this.x, this.y + 1);
  
  public Coordinate getBottom() { return this.bottom; }
  // ...
  public Coordinate copy(int x, int y) { //... }
for your second example:
Copy code
public class Coordinate {
  public Coordinate getBottom() {
    return this.copy(this.x, this.y + 1);
  }
  
  // ...
  
  public Coordinate copy(int x, int y) { //... }
(I left out the constructor and other getters for brevity)
t
Thank you @Riccardo Lippolis that makes sense.
Copy code
data class Coordinate(val x: Int, val y: Int) {
  val bottom get() = copy(y = y + 1)
}
👍 1
this one works as expected
👍 1
r
for completeness, and to correct myself a bit: my second Java example actually corresponds to your last Kotlin example (having the property getter inside of the data class). In your second example, where you define
bottom
as an extension function on the
Coordinate
class, the class itself would not contain any
getBottom()
function, but the function would be defined separately as a static method where the first argument would be a
Coordinate
instance, because that's how the Kotlin compiler implements extension functions on the JVM 😅
r
Another option (probably worse in your case) would be:
Copy code
data class Coordinate(val x: Int, val y: Int) {
    val bottom by lazy { copy(y = y + 1) }
}
Tradeoffs - the
get()
method creates a new instance of
Coordinate
every time, whereas the
by lazy
method only creates it once - but the
by lazy
method has synchronized initialisation logic (more expensive) and has to check whether it has been initialised every time you access it. If you call
bottom
rarely (or only once?) on an instance
get()
is better. If the result of
bottom
were to be mutable (obviously in your example it isn't) you might want to use
lazy
. If creating
Coordinate
is expensive (obviously in your example it isn't) and/or
bottom
is called a lot you might want to use
lazy
.
👍 1
(Just offering it up as another way of dealing with infinite recursion)
r
if you want lazy initialization, but accept the risk of possibly calling the initializer from several threads, you can also use:
Copy code
val bottom by lazy(mode = LazyThreadSafetyMode.PUBLICATION) { copy(y = y + 1) }
in this case, there's no synchronization, but an
AtomicReferenceFieldUpdater
is used to set the initialized value in the
Lazy
object or if you don't care about multithreading at all (use at your own risk!):
Copy code
val bottom by lazy(mode = LazyThreadSafetyMode.NONE) { copy(y = y + 1) }
that way, no locks/synchronization is used, but if you attempt to access the lazy value from different threads, the behaviour is undefined (so don't use this one unless you're absolutely sure the value is only being accessed from a single thread)
but maybe we're digressing now 😅
r
That's cool, didn't know about the different lazy modes before!
r
handle with care! 😁
t
thank you, I learned a lot today 🙂
👍 1