Thomas
07/16/2024, 6:33 AMfun main() {
val a = A(1)
val b = B(2)
println(b.toA()) //A(a=2)
println(A.fromB(b)) //A(a=2)
}
data class B(val b: Int)
data class A(val a: Int){
//1. "static converter" function
companion object{
fun fromB(b: B): A = A(b.b)
}
}
//2. extension function
fun B.toA() = A(this.b)
Thomas
07/16/2024, 6:36 AMJoffrey
07/16/2024, 6:57 AMString
), it might be better to avoid auto-completion pollution and use a factory function.
That being said, this is not a hard rule for me, and I pretty much choose by "feeling" as well. I'd be interested in knowing what other people do.Thomas
07/16/2024, 7:03 AMSetting.from(choice)
is similar in style with Setting.DEFAULT
Setting.OPTION_A
Setting.OPTION_B
etcJoffrey
07/16/2024, 7:06 AMRob Elliot
07/16/2024, 8:33 AMb?.toA()
is so much nicer than if (b != null) A.from(b) else null
phldavies
07/21/2024, 4:18 PMb?.let(A::from)
Rohde Fischer
07/26/2024, 9:54 AMdata class Foo
and some DTO wrapper, i.e. for serializing json:
data class FooDto
I'd ask if the conversion belongs naturally to the object, i.e. if I want to convert from the domain object having Foo(...).toDto()
I'd ask if it makes sense domain wise for Foo
to know about serialization (in my experience the answer here is almost always no). If the answer is no, I'd go with extension function living at the DTO. If yes then companion object
Similar on the DTO, here my experience is that the DTO is almost always a natural wrapper for a/some specific domain object(s) and thus often go with the companion object
personally I think that there's currently an overuse of extension methods, having a mess of imports cluttering up the code, for the sake of abstractions that are almost always included anyway and feels like a natural part of the domain in question
because of that feeling of overuse, I mostly go with modelling on the domain first if in doubt. And after all it's fairly easy to go from domain to extension if it turns out that it's the more natural solution