Your team of exceptional Java software engineers

Your team of exceptional Java software engineers

The first 98 elements exist in nature, although some are found only in trace amounts and others were synthesized in laboratories before being found in nature.

We build fast, reliable fintech back-ends

New in Kotlin 1.3: inline classes

Tomek Wałkuski, 


Programming languages, when it comes to types, can be dynamic or static. Then, both categories can be weakly or strongly typed. It turns out, even statically and strongly typed languages are not all the same.

Let's consider an example domain model, a Contact. It holds a name, and an email. Both properties are text data, and, in languages like Java or Kotlin, can be modeled using String type:

data class Contact(val name: String, val email: String)

The code above is "stringly-typed". Why? This is both valid and meaningful:

val friend = Contact(name = "John Doe", email = "john@doe.org")

On the other hand, this is valid but meaningless:

val friend = Contact(name = "john@doe.org", email = "John Doe")

Languages like Ada promote a different approach. All values, even when the underlying representation is the same, should have a different type:

type Name_Type is new Unbounded_String;
type Email_Type is new Unbounded_String;

type Contact is
   record
      Name  : Name_Type;
      Email : Email_Type;
   end record;

Then this code is valid, meaningful and is type-checked at compile-time:

Friends_Name  : Name_Type  := To_Unbounded_String ("John Doe");
Friends_Email : Email_Type := To_Unbounded_String ("john@doe.org");

Friend        : Contact    := Contact'(Name => Friends_Name, Email => Friends_Email);

And this will fail at compile-time:

Friends_Name  : Name_Type  := To_Unbounded_String ("John Doe");
Friends_Email : Email_Type := To_Unbounded_String ("john@doe.org");

Friend        : Contact    := Contact'(Name => Friends_Email, Email => Friends_Name);

Of course, this solution can be applied to our Kotlin code but at a cost:

data class Name(val value: String)
data class Email(val value: String)

data class Contact(val name: Name, val email: Email)

Now the Contact API is typesafe. One can't provide Name("john@doe.org") as an email, or vice versa. Unfortunately data classes wrap the underlying value in a runtime object. It results in a performance hit due to heap allocations, and the fact the JVM treats primitive types with special care and optimizes them heavily.

Thankfully, Kotlin 1.3 introduces inline classes. They are declared using inline keyword, placed before the class keyword. An inline class must have a single property initialized in the primary constructor. At runtime, the wrapper type is erased, and the value is represented just as a raw property.

inline class Name(val value: String)
inline class Email(val value: String)

data class Contact(val name: Name, val email: Email)

It is important to note the inline classes feature is still marked as experimental, and subject to change. It can be enabled in Gradle by passing the proper argument to the Kotlin compiler:

compileKotlin {
    kotlinOptions.freeCompilerArgs += ["-XXLanguage:+InlineClasses"]
}

You can find more on the topic at the official Kotlin documentation page.