Private setter for var in primary constructor

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')"
}
1 Like

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.

2 Likes

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.

3 Likes

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.

Did not think about that. In that case I agree that the extra keyword should be both in the constructor and on the property

And in android @Parcelize

All parameters of the primary constructor must be properties ( val / var ), otherwise they can not be (de)serialized

  • If there is a non-property parameter, it is a compile-time error

What would you think about the following one?

owned field: Type

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.

3 Likes

What would be the purpose of this feature? How is it different from using a var with private setter?

var field: Type
    private set

I was referring to a private-set field in the primary constructor of a class. Not member fields within the block of the class

What about:

data class Example(val x: Type1, var y: Type2; private set, var z: Type3)

to indicate that y is a variable with private setter?
(even the syntax highlighter like it :slight_smile: )

2 Likes

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.

The ugliness arises from that syntax is unique and does not appear anywhere else

Wouldn’t it be much more “Kotlin like” if inside the get/set we would use “it” instead of “field”?

2 Likes

I overcome my resentment long ago. If feels quite natural now. It probably could be handled better, but after some time it is quite OK.

2 Likes

I liked the idea of ​​@davidecannizzo, simple and clear

owned field: Type
3 Likes

Agreed, the property system has a poor return on complexity budget. Worst part of Kotlin, right after coroutines.

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

Any update is going on with this feature? Private setter should be natively supported as it is the first step to keep encapsulation in any class.

2 Likes

I suggest taking a more general approach. You can allow to redefine getter or setter for properties using the overload keyword:

val prop: String

overload var prop: String private set
1 Like