The init block of the value class can contain processing.

@JvmInline
value class Value(val value: Int) {
    init {
        if (100 < value) throw IllegalArgumentException()
    }
}

This will be compiled into a method named constructor-impl in the bytecode.

When instantiating a value class using Java reflection, both constructor-impl and box-impl must be called in order to be equivalent to instantiating it on Kotlin. On the other hand, there is no evidence that constructor-impl is called in the spring-framework, and only box-impl seems to be called.

Is this an intentional design?

Comment From: sdeleuze

I have reached Kotlin team to get related guidance.

Comment From: efemoney

Coroutine handling (in CoroutineUtils.invokeSuspendingFunction) calls box-impl directly. Ideally that would call the value class single primary constructor gotten from kotlin reflection.

Comment From: sdeleuze

@efemoney Unless I am mistaken, and as confirmed by the Kotlin team, this is not possible due to #31698.

Comment From: efemoney

Yeah I saw that but its slightly different from what I am saying which is

value class PlanId(val value: Int) {
  init {
    require(value >= 0) // Throw if value is negative
  }
}

assertEquals(
  PlanId(21),
  PlanId::class.primaryConstructor!!.call(21),
)

assertFails {
  PlanId::class.primaryConstructor!!.call(-21)
}

The only issue is this will need to use kotlin reflection KClasses.getPrimaryConstructor(kClass).call(args[index]), I dont know if thats desired or not

Comment From: sdeleuze

Good point, should be ok to use this.