While converting legacy Java code to Kotlin, I enc...
# announcements
m
While converting legacy Java code to Kotlin, I encountered this unpleasant effect: https://pl.kotl.in/-aERlNLZ0
Copy code
data class A(var b: B? = null)
data class B(var a: A? = null)

fun main() {
   val a = A()
   val b = B(a).also { a.b = it }
   println(a) // produces StackOverflow as the two toString impls call each other indefinitely
}
I suppose this is unavoidable - nevertheless, it can kill an application merely by converting a plain Java class into a data class. Any thoughts?
The obvious solution is to write custom toString implementations for complex data objects. But could there be another way? Maybe. If there was a second toString function that Kotlin would look for from inside the “normal” toString, then we could implement that for our nested data objects:
fun toStringFromToString() = id.toString()
This would solve the problem neatly for JPA entities, which is where I encountered it.
Or how about this:
fun toShallowString() = id.toString()
The contract could be that this implementation would not call any
toString
functions of member variables, but only their
toShallowString
implementations instead.
e
What was the purpose of making them
data
classes?
m
In our use case, they were JPA entities. It seemed like best practice to make those data classes, inspired by a Baeldung article. From a usability standpoint, mainly the copy and equals implementations come in handy.
Bad news: Of course the hashCode implementation also runs into a StackOverflowError …
j
Data classes are really a kind of syntactic sugar. If you have specific requirements on the toString-method, you'd probably be better of just using ordinary classes. As a side note, it is not a terrible idea to avoid designing classes with circular dependencies. 🙂
m
Those circular dependencies are built into the concept of JPA.
j
Yikes. Ok.
m
If you have two database tables with a 1:n relationship, the Entity at the n-end will have a reference to the Entity at the 1-end. The entity at the 1-end will have a collection of the entities at the n-end.
I agree with your and @elizarov’s basic notion that data classes may be a bad fit there. On the other hand, they are recommended for entities by various tutorials. I wonder who else ran into this problem, couldn’t find anything on the net.
t
In our use case, they were JPA entities. It seemed like best practice to make those data classes, inspired by a Baeldung article. From a usability standpoint, mainly the copy and equals implementations come in handy.
this part I really don’t get it though, I don’t feel data classes marry well with jpa. for once, I would consider equality more id-based than content-based. the official spring tutorial (https://spring.io/guides/tutorials/spring-boot-kotlin/) in the jpa section also mention
Copy code
Here we don't use data classes with val properties because JPA is not designed to work with immutable classes or the methods generated automatically by data classes
m
The devil’s in the fine print 😉 Here’s the Baeldung tutorial demonstrating JPA with immutable data classes. https://www.baeldung.com/kotlin/jpa
This is a possible solution. I guess there is no other way to get the copy method than to define a data class? https://pl.kotl.in/1Q5ORUC5W
d
i successfully used data classes in a huge project with hibernate. so i think official docs are confusing here.
t
to that line, I used regular classes in projects with hibernate successfully. I think it is conceptual matter more than a practical one (though I had issue in a couple of cases with data classes, now I don’t remember what). for me it boils down to data classes caring about values, entity caring about ids. to me, I want entities with same id to be related in hibernate, and with data classes that is not given