Why can not I inherit a lot of data class from another data class, in my case in the set of data classes there are several identical fields and I want to put them in a separate class and inherit from it, but the Kotlin does not allow it.
General class
@XmlAccessorType(XmlAccessType.PROPERTY)
data class Model(@XmlElement var name: StringProperty, @XmlElement var id: StringProperty) {
constructor() : this(SimpleStringProperty(), SimpleStringProperty())
fun getName(): String {
return name.get()
}
fun setName(name: String) {
this.name.set(name)
}
fun getId(): String {
return id.get()
}
fun setId(name: String) {
this.id.set(name)
}
}
//@XmlAccessorType(XmlAccessType.PROPERTY)
//open class Model(@XmlElement var name: StringProperty, @XmlElement var id: StringProperty) {
//
// constructor() : this(SimpleStringProperty(), SimpleStringProperty())
//
// fun getName(): String {
// return name.get()
// }
//
// fun setName(name: String) {
// this.name.set(name)
// }
//
// fun getId(): String {
// return id.get()
// }
//
// fun setId(name: String) {
// this.id.set(name)
// }
//}
Other class
@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
data class MyOtherData(@XmlElement val service: StringProperty) : Model() {
constructor() : this(SimpleStringProperty())
fun getService(): String {
return service.get()
}
fun setService(service: String) {
this.service.set(service)
}
}
In the future, I need to copy the data class and serialize it in Xml, to save it to a file.
Xml serializer
object Xml {
fun serialize(value: Any): String {
val context = JAXBContext.newInstance(value::class.java)
val marshaller = context.createMarshaller()
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true)
StringWriter().use { stream ->
marshaller.marshal(value, stream)
return stream.toString()
}
}
fun <T> deserialize(value: String, clazz: Class<T>): T = StringReader(value).use { stream -> return JAXB.unmarshal(stream, clazz) }
}
Main.kt
val model = MyOtherData()
model.setService("OTHER_SERVICE")
model.setName("OTHER_NAME")
model.setId("3fad6db0ae684a0699c8ff0898e46d55")
// serialization/deserialization XML
val xml: String = Xml.serialize(model)
println(xml)
val des = Xml.deserialize(xml, MyOtherData::class.java)
val copy = model.copy()
println(copy.getService())
println(copy.getName())
println(copy.getId())
Can make a copy through serialization/deserialization, but i thought the data classes can be inherited.
You can inherit a data class from a non-data class. Inheriting a data class from another data class is not allowed because there is no way to make compiler-generated data class methods work consistently and intuitively in case of inheritance.
Yes, that’s correct. It’s impossible to generate a copy() method that could copy the properties of a non-data class, because there is no way to establish the correspondence between constructor parameters and properties.
Are there any examples of this that you can link to? I have tried several variations, but each one seems to give a different compiler error. Property is hidden, property needs override, property is final and cannot be overridden, etc.
I don’t have examples at the moment.
Yes, I realize that I’m replying to an old post.
EDIT: I went back and tried again. Used the IntelliJ tools to fix the problem. The properties in the base class had to be declared as open in order to inherit properly.
Is there any way to fix the comment section problem?
I changed Parent to sealed class, but still, it’s the same.
package day1
object Main {
@JvmStatic
fun main(args: Array<String>) {
val f1 = Foo(1)
Thread.sleep(3)
val f2 = Foo(1)
val p1 = (f1 as Parent)
val p2 = (f2 as Parent)
println(p1 == p2) // true
println(p1.b == p2.b) // false
}
}
data class Foo(val a: Int) : Parent("$a-${System.currentTimeMillis()}")
open class Parent(val b: String) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Parent
if (b != other.b) return false
return true
}
override fun hashCode(): Int {
return b.hashCode()
}
}
When I override Foo.equals() and Foo.hashCode() then it works correctly. So I guess, probably default generated code for data class is wrong.
package day1
object Main {
@JvmStatic
fun main(args: Array<String>) {
val f1 = Foo(1)
Thread.sleep(3)
val f2 = Foo(1)
val p1 = (f1 as Parent)
val p2 = (f2 as Parent)
println(p1 == p2) // false
println(p1.b == p2.b) // false
}
}
data class Foo(val a: Int) : Parent("$a-${System.currentTimeMillis()}"){
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (!super.equals(other)) return false
other as Foo
if (a != other.a) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + a
return result
}
}
sealed class Parent(val b: String) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Parent
if (b != other.b) return false
return true
}
override fun hashCode(): Int {
return b.hashCode()
}
}
Data classes are intended for simple value classes whose state is entirely characterised by the properties in the constructor. If that doesn’t apply, then a data class may not be a good fit, because the autogenerated equals(), hashCode(), toString(), copy(), and componentX() methods may not do what you want.