Do you really need data class? Maybe something like this will work?
class Foo(id: Int, name: String) {
var id: Int = id
private set
var name: String = name
private set
override fun toString(): String = "Foo(id=$id, name='$name')"
}
Maybe instead of having an option for a custom getter/setter in the primary constructor it could be possible to annotate properties so they are treated as if they are part of the primary constructor. Something like this
data class Foo(id: Int, val name: String) {
@Data
val id: Int = id
}
In that case data classes would also need to allow parameters which are not directly defined as a property, but I don’t see any good reason why this must be the case.
No, i don’t. But a data class come with equals and hash and copy. Equals and hash are required for what I am doing. I would end up using lombok or rolling my own if I didn’t.
I am starting to feel like Java actually does a better job than kotlin on this
I’m not sure your annotation is the best approach. In terms of syntax it is clear that all this special stuff cannot really be done inside the primary constructor. For normal classes that isn’t really an issue, but for data classes this is. In both cases what you want to do is say that it is a property but it’s specification is deferred. Why not have something like:
data class Foo(deferred id:Int, val name: String) {
deferred var id: Int
private set
}
I’m not sure that repeating deferred on the definition as well is needed, but I think it is better as it signals that the value is set from the primary constructor as usual. For data classes the usual rules on data classes apply. Deferred properties obviously also have to have an underlying field.
I like your idea better. That being said I agree with you. It would be enough to have the deferred keyword in the constructor I think. The only problem I see in that case is that you don’t actually see that the property is being set by the constructor. But I guess that only really matters in data classes which are really big and because data classes should not have any “complex” functionality this should not matter.
The idea would apply to all classes, so I would still favour the extra annotation on the actual property but it is 100% a readability issue, the compiler wouldn’t need it.
My idea is basically about the keyword owned, which would mean that, since the field is owned by the class, only that’s got the right to change its value.
Anyway, if owned can’t be stood with, you could think about any other keyword that makes sense.
Coming across this cause running into a similar issue, I think the best solution would be to create an annotation that could represent the access level. then your could specify custom access levels using the already existing mechanism for specifying annotations on properties, which already work in constructors.
so you could have
data class myDataClass(@set:Private var someVar)
There is no new syntax required, just annotation handling by the compiler.
Being able to specify a private (or protected) set on a primary constructor parameter is still a feature I would like to see in Kotlin.
The data class example that @Christian.Bongiorno gave is a good example - and indeed, you could make it private and then define a second, public field to get its value.
However, it seems to me that the whole point of Kotlin’s get and set is to move away from the amount of unnecessary repetition / lines of code that the Java-style private member with public getter created.
The following (or other suggestions) may not be syntactically the prettiest:
data class Foo(private(set) var id: Long)
but, in my opinion, the alternative:
data class Foo(private var _id: Long) {
val id get() = _id
}
is also not pretty syntax.
This behaviour is also relevant to Kotlin’s @Parcelize annotation, which can only apply to classes with primary constructors whose arguments are either var or val.
For a library I am working on, we have to decide between having these properties as public var even though we don’t really want them to be modifiable from outside the class body, or to have them as protected var but then write an extra property for each in order to publicly expose its value, creating extra lines of code that essentially duplicate what we’ve already defined