5

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.

4 Answers 4

8

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Looks like your example does not present the full picture: in the code you present, a no-argument version of copy() is not available for Java at all, and the phrase about defining copy() explicitly does not seem relevant. In any case, the design of copy() is based on default parameters mechanism that is only available in Kotln, not in Java
The copy() function does exists in Java for the example above, it just doesn't copy any values since there are no arguments for the class. I didn't mention explicitly defining a copy function. I just didn't fully read the documentation of data classes and misinterpreted how they work.
Oh, I'm sorry: didn't copy the code of your example and misread curly braces for round ones. My bad
2

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)
    }

}

1 Comment

This would defeat the purpose of data classes
1

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
}}

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.