Home > Software engineering >  Why can I not delegate to a parameter in a Kotlin value class?
Why can I not delegate to a parameter in a Kotlin value class?

Time:07-17

I can delegate to a contructor parameter in a class

class SerialNumber(val value: String) : CharSequence by value {

    init {
        require(value.isNotBlank())
    }
}

but if I make the class a value class

@JvmInline
value class SerialNumber(val value: String) : CharSequence by value {

    init {
        require(value.isNotBlank())
    }
}

then the compiler says "Value class cannot implement an interface by delegation if expression is not a parameter"

As far as I can see value is a parameter - what is going on?

CodePudding user response:

Implementation by delegation for inline classes was enabled in version 1.7.0

https://kotlinlang.org/docs/whatsnew17.html#allow-implementation-by-delegation-to-an-inlined-value-of-an-inline-class

If you want to create a lightweight wrapper for a value or class instance, it's necessary to implement all interface methods by hand. Implementation by delegation solves this issue, but it did not work with inline classes before 1.7.0. This restriction has been removed, so you can now create lightweight wrappers that do not allocate memory in most cases.

The issue is here: https://youtrack.jetbrains.com/issue/KT-27435

Before Kotlin 1.7, it is not possible to implement an interface by delegation, and you will get an error (and, as you note, a nonsensical one at that).

If you are unable to update to 1.7, you can manually delegate to the value. Thankfully Kotlin keeps its class-footprint small by utilising extension functions, so this is pretty easy! You just need to delegate one property, and two functions.

@JvmInline
value class SerialNumber(val value: String) : CharSequence {

  init {
    require(value.isNotBlank())
  }

  override val length: Int
    get() = value.length

  override operator fun get(index: Int): Char = value[index]

  override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
    value.subSequence(startIndex, endIndex)
}
  • Related