data class C(private val _propertyWithSetter: String) {
val propertyWithSetter: String
get() = _propertyWithSetter
set(value) {
/* execute setter logic */
_propertyWithSetter = value
}
}
But the name of constructor argument starts with “_” is quite a problem. (eg. named argument call, copy, …)
And if we have many properties many boilerplate will made.
I think there should be a better way for ‘private set applied to constructor var’.
I’m facing something very similar, in which I need to modify a parameter on its initial construction, and would prefer to use a data class. In particular, I am writing a Direction class for a three-dimensional unit vector using the Apache Commons Math library Vector3D; the norm of the vector should always be one. What I wish I could write is something like:
data class Direction(val vector: Vector3D set(value) { field = vector.normalize() } ) { ... }
As it is I am not using a data class, and thus am re-implementing my own equals(), hash(), …
I have similar problem. Firebase cant parse my data class completly. It can parse all params except for one var is_captain:String? = null
I tried to write custom get and set as mentioned above
data class Member(var _is_captain: String? = null) {
{
var is_captain: String?
get() = _is_captain
set(value) {
_is_captain = value
}
}
but it still throws ClassMapper: No setter/field for is_captain found
class User( nameParam: String ) {
var name : String = nameParam // why is nameParam saved directly into name.field here ?!?
set(value) {
println("Set is invoked")
field = value.toUpperCase() + "!!!"
}
}
fun main(args: Array<String>) {
val ivan = User("Ivan") // <<- setter is not invoked here!
println(ivan.name)
ivan.name = "Ivan" // <<- but now it is invoked
println(ivan.name)
}
Thank you for explanation. This behavior is not very clear though. I guess you use it to initialize internal field, right?
So, to achieve my aim I want to use init {} block:
class User(nameParam: String) {
var name : String = <init_value>
set(value) { ... }
init {
name = nameParam
}
}
It does in fact say in the documentation that the initializer value is written directly to the backing field.
Consequently, if you do want the setter to be called, you have to do this in an init block as you’re now doing.
However, an unsatisfactory aspect of doing this is that you still need to provide an initial value for the backing field which is therefore, in effect, being initialized twice. You can’t, of course, use lateinit if there’s a custom setter.
Whilst the choice of initial value isn’t really a problem for primitives or String (you can always use "" for the latter), it could be a problem for other types and you may therefore have to resort to making the property nullable.
I’m not aware of any way to avoid this double initialization.
Whilst your approach should work, I think it might be a case of the cure being worse than the disease!
It dawned on me after making my previous post that, if you’re going to call the setter in init, then you might as well always initialize the property to exactly the same value. So there isn’t really a problem choosing a suitable initial value even for non-primitive types.
The double initialization problem remains (as, frankly, it does in many other OO languages) though at the end of the day it’s not going to cost us many clock cycles
If you want to use a data class you can use this workaround
data class Direction(var vector_: Vector3D) {
init {
vector_ = vector_.normalize()
}
val vector = vector_
}
not the best but the data class relies on the modified version of your vector. The name in the constructor is slightly different but you can’t access vector in the constructor so no error can be made and you don’t lose the power of the data class (hash and equals) nor the immutability
This example is terrible. Firstly vector is not updated when you change vector_. Secondly you shouldn’t declare properties with backing fields in data classes except in the primary constructor.
If you really need something like this use
data class Foo(private var _test: Vec3) {
var test: Vec3
get() = _test
set(v) {
_test = transform(v)
}
init {
_test = transform(test)
}
}
sorry I forgot the private in the constructor in my example. data class Direction(private var vector_: Vector3D) {
Adding the private in my case shows the case of an immutable data class with custom transformers whereas yours shows a mutable data class with custom transformers.
Thanks for calling my previous example terrible
Yeah, well always happy to help Maybe “terribel” is somewhat of an overreaction but reopening a topic after a year to add an example which is wrong is, well