Why?
One key use case for this will be with object-relational mapping engines (ORMs) and similar systems, for helping to avoid “stringly-typed” properties.
Suppose, for example, that we have a database that has invoices, where invoices have a series of line items representing the things to appear on the invoice. In Kotlin, we might have Invoice
and InvoiceLineItem
classes to map to the tables in our database. And those classes might have properties that line up with the columns in the table… using data types based on the column types:
data class Invoice(
val key: String,
val createdOn: Instant,
val customerKey: String,
// TODO other properties
)
data class InvoiceLineItem(
val key: String,
val quantity: Int,
val productKey: String,
// TODO other properties
)
The problem here is that all of our keys are String
properties:
- the key on
Invoice
- the key on
InvoiceLineItem
- the key to our customer, perhaps in a
Customer
class and table - the key to our product, perhaps in a
Product
class and table
From Kotlin’s standpoint, a String
is a String
is a String
. We as developers know that we cannot use an InvoiceLineItem
key to look up a product… but Kotlin does not know that, so it cannot enforce such a restriction. This sort of “stringly-typed” set of properties leads to bugs, where we accidentally use one class’ key where we meant another class’ key, and Kotlin lets it happen because they are all strings.
With inline class
, we can wrap those strings in classes that are distinct:
data class Invoice(
val key: InvoiceKey,
val createdOn: Instant,
val customerKey: CustomerKey,
// TODO other properties
)
data class InvoiceLineItem(
val key: InvoiceLineItemKey,
val quantity: Int,
val productKey: ProductKey,
// TODO other properties
)
Each of those ...Key
classes is an inline class
akin to the one shown earlier in this chapter:
inline class InvoiceLineItemKey(val key: String)
At compile time, Kotlin treats all four of those ...Key
classes as being distinct and not equivalent. We cannot accidentally use a ProductKey
where we need an InvoiceKey
, for example. This eliminates a class of bugs through strong type checking in the compilation process.
However, because they are inline class
, at runtime, they are just strings. We do not incur overhead of creating instances of all of these ...Key
classes. So, we gain the power of type checking without paying a price when our app runs.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.