Private setter for var in primary constructor


#1

Is there a way to specify private set for a var in the primary constructor?

If not, is there a reason this is not possible? It’s the most common reason in our code why we have to move a member into the body of a method.


#2

No, it’s not possible because we don’t see a good syntax for it. If you have suggestions, feel free to show what you have in mind.


#3

You’re right. I wouldn’t mind having var field: String private set, but pretty is something else.


#4

I suggest something akin to Swift: private(set) var field: String, get on the other hand is public by default


#5

I think that property getters and setters are already bad, no need to get them worse. In my opinion, property accessors is the only ugly thing in kotlin design. The ugliness arises from that syntax is unique and does not appear anywhere else (3 special keywords and a whole new entity).

Assigning property with private setter requires only a few additional words. It does not worth breaking syntax for it.


#6

Normally you define a completely private variable as private var field: String, I don’t see how adding (set) would be that ugly if ugly at all. It’s simple and concise.


#7

I prefer this too (Swift style), both for constructor param specification, as well is in the class body. Currently, to define a private set var with kotlin in one line requires a semicolon.


#8

Data classes must have at least 1 argument in their primary constructor and you can’t make private set on it.

So, on a data class, no matter what you do, you can’t have a var that is internally (only) modifiable and expose just a getter. You automatically expose a setter which I don’t want

I want the java equivalent to:

public class Foo {

    private Long id;
    
    public Long getId() {return id;}
}

I don’t see how


#9

How about this?

data class Foo(private var idField: Long) {
    val id get() = idField
}

#10

Ok, that definitely gets me closer - When I throw an interface in things get tricky

interface Post  {

    val id: Long?

    val date: Date?

    val name: String?
}
data class Report( private var idData: Long? = null) : Post {
    override var id get() = idData
    override var date: Date? = null
    override var name:String? = null
}

Gets me: Error:(10, 5) Kotlin: Property must be initialized for id.


#11

Change the var of id to val, then it works.

Or, if you want to really edit it from the outside:

 override var id
        get() = idData
        set(value) {
              idData = value
        }

If you omit the setter like this:

 override var id = idData
     get() = idData

Then you get an additional backing-field which is set to idData on init. If you read id you will get idData, because of the getter, but if you set it from the outside, you will set the backing-field, which is never read anywere.

For sake of completeness if you want to set the backing-field AND the other field in the constructor you can do this:

 override var id = idData
     get() = idData
     set(value) {
          idData = value
          field = value
     }

#12

By the way, I don’t know what you need, but I wouldn’t use nullable references in an interface.

Are you sure you don’t need something like this:

    interface Post {
        val id: Long
        val date: Date
        val name: String
    }

    data class Report(override val id: Long) : Post {
        lateinit var dbRow: DBRow
        
        override val date 
             get() = dbRow.getDate(id)...
        override val name 
             get() = dbRow.getName(id)...
    }

If you want to set the fields on the Report from the outside, you can also use lateinit

interface Post {
    val id: Long
    val date: Date
    val name: String
}

data class Report(private var idData: Long) : Post {
    override val id get() = idData
    override lateinit var date: Date
    override lateinit var name: String
}

#13

I am trying to make an object compatible with hibernate. Hibernate is dirty and does member reflection and private member injection. This was of course always dirty (java 9 warns on this) and there is an open ticket with them to allow constructor hydration of objects.

My objective is to port some working Java code to demonstrate Kotlins capabilities as part of a push to modernize our development.

Now, I realize that hibernate is not ideal for kotlin, and I will push for something like Exposed (which currently doesn’t support hsql).

I am going to continue to mess with what you have supplied - it’s vastly superior to anything I have come up with.

probably best to continue this on the slack channel I am chb0kotlin


#14

In that case I would probably use kotlinx serialization. Write an adapter that takes the DB row, converts to jspn and reads back.