0

Newby in Kotlin, I'm trying to implement a simple data class with constraint validation in fields.

This works great, but my solution is suboptimal since it exposes the private variables names defined in the class' definition in the toString representation, where I would prefer to have the properties:

data class MutablePointKt(private val _x: Int = 0, private val _y: Int = 0) {
    private fun validatePositiveOrZero(some:Int) {
        Validate.isTrue(some >= 0, "negative coordinate provided: $some")
    }

    var x: Int = 0
        get() {
            println("  > getting x: $field")
            return field
        }
        set(value) {
            validatePositiveOrZero(value)
            field = value
        }
    var y: Int = 0
        get() {
            println("  > getting y: $field")
            return field
        }
        set(value) {
            validatePositiveOrZero(value)
            field = value
        }

    init {
        this.x = _x;
        this.y = _y;
    }
}

println(MutablePointKt(1, 2)) // prints "MutablePointKt(_x=1, _y=2)". how to print "MutablePointKt(x=1, y=2)" ?

Thank you !

EDIT:

I have a solution with override fun toString(): String = ToStringBuilder.reflectionToString(this, KotlinToStringStyle()) and

class KotlinToStringStyle : ToStringStyle() {
    private fun isFiltered(s: String?) = s?.startsWith("_") == true

    override fun appendFieldStart(buffer: StringBuffer?, fieldName: String?) {
        if (!isFiltered(fieldName))
            super.appendFieldStart(buffer, fieldName)
    }

    override fun appendDetail(buffer: StringBuffer?, fieldName: String?, any: Any) {
        if (!isFiltered(fieldName))
            super.appendDetail(buffer, fieldName, any)
    }

    override fun appendFieldEnd(buffer: StringBuffer?, fieldName: String?) {
        if (!isFiltered(fieldName))
            super.appendFieldEnd(buffer, fieldName)
    }
}

... but this is rather overkill, I would prefer a concise solution aka "the Kotlin way"

spi
  • 626
  • 4
  • 19
  • Sorry I don't understand what you mean by "it exposes the private variables names defined in the class' definition in the toString representation, where I would prefer to have the properties" – m0skit0 Mar 22 '21 at 09:09
  • @m0skit0 it prints ```MutablePointKt(_x=1, _y=2).``` Note the underscores. These are private values passed in what seems to be a constructor argument. They are then leaked in the toString representation, although it is flagged as private and are not properties (I should be missing some important concept here I think). – spi Mar 22 '21 at 09:20
  • [This question](https://stackoverflow.com/questions/38492103/override-getter-for-kotlin-data-class) seems very relevant. – gidds Mar 22 '21 at 09:43
  • So the answer is "dont do this" ? I thought of data classes to be more flexible, that seems not to be the case though. As long as you need a dumb implementation (ie. no validation, just an holder for raw data) this is fine, if you need more logic, go for a standard class and implement the boilerplate code yourself. Am I right ? – spi Mar 22 '21 at 09:58
  • 1
    @spi Only you can judge your case; but yes, that seems a reasonable assessment.  Data classes are just a useful shortcut, and you may not need all that they give you.  Writing your own `toString()` could be a one-liner; your own `equals()` and `hashCode()` (if appropriate) should take only a few lines.  You're less likely to need the `componentN()` methods, nor `copy()`.  And if it's a complex class, you may already have factory methods (or even a builder), so might not need the primary constructor either.  Yes, it's not ideal, but that's how it is! – gidds Mar 22 '21 at 12:05
  • Ok that's fine for me. It reminds me the lib lombok in java - works fine as long as you need only dumb implem. I'll go this path then :) – spi Mar 22 '21 at 12:10

0 Answers0