Private setter for var in primary constructor

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.

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
     }
1 Like

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
}
1 Like

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

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

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.

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.

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: )

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”?

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.