Hey folks, I'm looking into data classes and I wan...
# server
j
Hey folks, I'm looking into data classes and I wanted to verify that
copy
creates shallow, not deep copies. The math checks out for complex types such as lists or other data classes, but not for 'primitives' likes Strings and Enums. Can someone help me clarify this? [Here is a complete example](https://pl.kotl.in/CzsYyKC_l) , but in essence for the given code, I would potentially expect
name
to also be mutated in the copy if data classes were purely shallow. Am I getting this wrong?
Copy code
import java.util.*

enum class WEAPONS{
    AXE, SWORD, WAND, BOW
}

enum class CLASS{
    WIZARD, WARRIOR, PALADIN, THIEF
}

data class Origin(val city: String, var country: String)

data class FantasyHero(var name: String, val weapons: MutableList<WEAPONS>, var heroClass: CLASS?, val origin: Origin = Origin("Utrecht", "The Netherlands"))

fun main(){
    val gandalf = FantasyHero("Gandalf the Grey", mutableListOf(WEAPONS.WAND), CLASS.WIZARD)
    val anotherGandalf = FantasyHero("Gandalf the Grey", mutableListOf(WEAPONS.WAND), CLASS.WIZARD)
    val gandalfCopy = gandalf.copy()
    val betterGandalf = gandalf.copy(name="Gandalf the White")

    println("--------")
    println("Changing the first instance's name")


    gandalf.name = "Gandalf the White"
    println(gandalf.name)
    println(anotherGandalf.name)
    println(gandalfCopy.name)
    println(betterGandalf.name)

    println("--------")
    println("Changing the first instance's class")

    gandalf.heroClass = CLASS.PALADIN
    println(gandalf.heroClass)
    println(anotherGandalf.heroClass)
    println(gandalfCopy.heroClass)
    println(betterGandalf.heroClass)
}

--------
Changing the first instance's name
Gandalf the White
Gandalf the Grey
Gandalf the Grey
Gandalf the White
--------
Changing the first instance's class
PALADIN
WIZARD
WIZARD
WIZARD
s
This is quite a long example; is there a specific line or part of the example that’s behaving in a way you don’t expect?
Your understanding that
copy
creates shallow copies is correct
l
i think this is a terminology issue
j
@Sam, much shorter example ->
Copy code
data class Origin(var city: String, var country: String)


fun main(){
    val myHome = Origin("Utrecht",  "the Netherlands")
    val myHomeCopy = myHome.copy()
    
    myHome.city = "Amsterdam"
    println(myHome.city)
    println(myHomeCopy.city)

}
If we are talking about pure shallow copies, I'd expect myHome.city and myHomeCopy.city to print the same value here (since they reference the same location in memory, right?)
l
i'd expect to see:
Copy code
Utrecht
Amsterdam
j
Except I get Amsterdam, UTrecht
s
The Kotlin/Java memory model is a bit different from what you’re picturing, I think
j
Now, for anything more complex (Lists, other data class, Set, ...), The values do get mutated in the copies as well
s
Think of it more like a pointer
☝️ 1
l
copy creates a new instance with the same top-level (shallow) values
the "values" for a list or set are the references to them
j
So, if we do think of it as pointers indeed, how is it that I don't get "Amsterdam, Amsterdam"? I would expect both String pointers to point to the same value ?
s
References in Kotlin are immutable — if you change a variable you’re pointing it at a new reference, not changing the existing reference
l
not a pointer to the copied object, but a pointer to the property
(sam i saying this better than me 🙂)
s
Copy code
var a = "1"
val b = a
a = "2"
println(b) // "1"
same concept, without the data class
copy
being involved
the reference is always immutable; all you can do is point at a different object reference instead
Nothing you do to
a
can change which object
b
points to
j
🧐
Right
Yeah I think I got it. The behaviour will be the same for anything that you can set like this. So enums have the same behaviour
👍 1
Got it, ty
s
It catches out a a lot of newcomers to Java tbh, I think people who started out on Java don’t always realise how different it is from other languages
People often ask “is Java pass by reference or pass by value”
And the answer is actually “it’s pass by value but the value is a reference” 😂 🤦
💯 5
j
oh well, I only have 10 years of experience xD
😄 1
Thanks! Good to come back to the basics sometimes clearly
l
@Sam you are only the second java dev i have heard say that (i am the other one) 😄
1
😄 1
“it’s pass by value but the value is a reference” - always always always pass by value 😄
1
c
Unrelated to your question, @julien lengrand-lambert, but your code has a lot of
var
and mutable list that don't seem necessary.
j
Thanks @cedric. you're very correct, it's actually the point. I typically use only vals but use vars explicitely to mutate things around and see the behaviour of those 😊
c
While I think that
val
and immutable structures are a great default starting point, writing a lot of Rust these past years has made me more comfortable embracing the mutable nature of things as long as the compiler makes sure I'm not writing stupid code 🙂
👀 1
In much the same way that Kotlin has made me more comfortable embracing
null
values