Hi,
I just learned about Kotlin’s new inline class feature today. It got me excited right away, because it enables domain-driven design without the cost of the thin-wrapper-objects.
One thing that I find rather restrictive in the current specification is that an inline class does not permit an init
block. I can imagine that there are several reasons for that:
- The compiler requires the ability to wrap and unwrap the inline class as needed.
- Side effects in the
init
block will lead to desaster and uncontrolled behaviour.
However, having no init
block at all also removes an important capability of classes: parameter checking.
Let’s for example assume we want to create an inline class for URL
, which wraps a single String
. What would you typically do in the constructor? Check the URL syntax and throw an exception if it’s wrong. Unless I’m missinig something here, you can’t seem to do that with Kotlin inline classes.
If we ignore the possibility of side effects for a moment, what would happen if we allowed an init
block for inline classes, and assume that the user only performs argument checking in that block? Well, sticking with the URL example, at some point the user has to do:
val url = URL("www.kotlinlang.org")
… which the compiler would translate to:
String url = "www.kotlinlang.org";
/* inlined parameter checks (init block) of the URL class here */
No harm done here. When the compiler runs into an explicit constructor call, it inlines the init
block, if it needs to perform auto-conversion, it skips the block (because the constraints have already been checked at all locations where a new instance of the inline class has been created; they have been shifted to the system boundary, where they really belong). Side effects in init
are the only real issue that I can see here; but that’s a massive anti-pattern anyway and definitly not a use case for inline classes.
I imagine that this could be very very useful. In Java, you are forced to decide:
- either you just use
String
, and get weaker/ambiguous signatures (you don’t want just anyString
but aURL
, but the type system can’t express that), - or you have to pay the overhead cost of the
URL
wrapper orbject.
Kotlin has the potential to allow for the best of both worlds, but at the moment unfortunately doesn’t offer this capability.
As a more syntactically restricted alternatives to init
blocks, what if the kotlin compiler would insert checks for javax.validation
-style field annotations? That way, we could perform range checks on numbers and length checks / regex checks on strings without needing an init
block:
@Min(3)
@Max(20)
inline class Name(val s: String)
val name = Name("John")
… would compile to:
String name = "John";
if(name.length() < 3){
throw new IllegalArgumentException("Name must have length 3 or more!");
}
if(name.length > 20){
throw new IllegalArgumentException("Name must have length 20 or less!");
}
If the set of such check annotations is predefined, no side effects need to be considered.
Any thoughts?