Reputation: 2600
Maybe I'm misinterpreting how the copy
function of a data
class works or maybe there's a bug, but the following is an example of the copy
function not working as expected:
Kotlin:
data class A {
public var x: String? = null
public var y: String? = null
public var z: B = B.ONE
}
enum class B {
ONE
TWO
THREE
}
Java
A a1 = new A()
a1.setX("Hello")
a1.setY("World")
a1.setZ(B.TWO)
A a2 = a1.copy()
// a2.x is null
// a2.y is null
// a2.z is B.ONE
It seems that copy
is just making a new instance of A
and not copying the values. If I put the variables in the constructor, the values are assigned, but then it's no different than constructing a new instance.
Upvotes: 5
Views: 15699
Reputation: 2444
This question is high in search rankings, and potentially confusing for those new to kotlin, since the question's sample code is not typical kotlin code or usage of the copy function. I added some sample code below to help clarify what's going on, and also show typical usage of a data class.
In short, the copy
function is most useful when called from a kotlin class. I agree that its behavior isn't obvious when called from java code.
//
// A.kt
//
// this is an idiomatic kotlin data class. note the parens around the properties, not braces.
data class A(
val x: String? = null,
val y: String? = null,
val z: B = B.ONE
) {
// this javaCopy function is completely unnecessary when being called from kotlin; it's only here to show a fairly simple way to make kotlin-java interop a little easier (like what Nokuap showed).
fun javaCopy(): A {
return this.copy()
}
}
enum class B {
ONE,
TWO,
THREE
}
fun main() {
val a1 = A("Hello", "World", B.TWO)
// here's what copy usage looks like for idiomatic kotlin code.
val a2 = a1.copy()
assert(a2.x == "Hello")
assert(a2.y == "World")
assert(a2.z == B.TWO)
// more typical is to `copy` the object and modify one or more properties during the copy. e.g.:
val a3 = a1.copy(y = "Friend")
assert(a2.x == "Hello")
assert(a3.y == "Friend")
}
public class App {
public static void main(String[] args) {
A a1 = new A("Hello", "World", B.TWO);
// the kotlin copy function is primarily meant for kotlin <-> kotlin interop
// copy works when called from java, but it requires all the args.
// calling the `javaCopy` function gives the expected behavior.
A a2 = a1.javaCopy();
assert a2.getX().equals("Hello");
assert a2.getY().equals("World");
assert a2.getZ().equals(B.TWO);
}
}
Official docs on data classes, including the copy
function:
https://kotlinlang.org/docs/reference/data-classes.html
Upvotes: 0
Reputation: 2379
For interop with java you can make function that use kotlin generated .copy
@Entity
data class User(@PrimaryKey var id: Int = 0,
var firstName: String? = null,
var lastName: String? = null,
var phone: String? = null,
var email: String? = null,
var phoneCode: String? = null,
var tokenId: String? = null,
var provider: SocialProvider? = null) : Serializable {
var countryCodeIso: String? = null
set(countryCodeIso) {
if (countryCodeIso != null) {
field = countryCodeIso.toLowerCase()
}
}
fun javaCopy(): User {
val user = copy()
user.countryCodeIso = countryCodeIso
return user
}}
Upvotes: 1
Reputation: 2553
What you can do to get around the limitations of Kotlin's copy(), is to create your own copy function inside your data class. Example below:
data class User(val name : String, val property: String) {
fun copy() : User {
//uses the fields name and property defined in the constructor
return User(name,property)
}
//or if you need a copy with a changed field
fun copy(changedProperty : String) : User {
return User(name, changedProperty)
}
}
Upvotes: 2
Reputation: 2600
Okay, I missed this sentence in the docs:
If any of these functions is explicitly defined in the class body or inherited from the base types, it will not be generated.
Which, infact, makes copy
no better than a constructor for Java interop.
Upvotes: 8