Todor Grudev
12/14/2022, 8:57 AMdata 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?Todor Grudev
12/14/2022, 9:00 AMdata class Coordinate(val x: Int, val y: Int)
val Coordinate.bottom: Coordinate get() = copy(y = y + 1)
this one works though π€·Riccardo Lippolis
12/14/2022, 9:00 AMbottom
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, ... etcRiccardo Lippolis
12/14/2022, 9:01 AMcopy
function only gets called when you get the bottom
property explicitlyRiccardo Lippolis
12/14/2022, 9:04 AMpublic 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:
public class Coordinate {
public Coordinate getBottom() {
return this.copy(this.x, this.y + 1);
}
// ...
public Coordinate copy(int x, int y) { //... }
Riccardo Lippolis
12/14/2022, 9:04 AMTodor Grudev
12/14/2022, 9:05 AMTodor Grudev
12/14/2022, 9:08 AMdata class Coordinate(val x: Int, val y: Int) {
val bottom get() = copy(y = y + 1)
}
Todor Grudev
12/14/2022, 9:08 AMRiccardo Lippolis
12/14/2022, 9:12 AMbottom
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 π
Rob Elliot
12/14/2022, 9:33 AMdata 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
.Rob Elliot
12/14/2022, 9:34 AMRiccardo Lippolis
12/14/2022, 10:23 AMval 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!):
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)Riccardo Lippolis
12/14/2022, 10:23 AMRob Elliot
12/14/2022, 10:24 AMRiccardo Lippolis
12/14/2022, 10:25 AMTodor Grudev
12/14/2022, 10:26 AM