- Everybody can agree on proper naming being important, this is about specific case I experienced
- Is
countryCode
a good name for a variable of typeString
?- Digits? Characters? How long?
- API sends numeric, e..g
"278"
for Germany - We used ISO-2 codes only, so
"DE"
(ISO-3"DEU"
were not used at all in my case so i’ll ignor them) - Not perfect but maybe a bit better
numericCountryCode
vsalphaCountryCode
- at least can distinguish between 278 and DE!
- Alternative: no primitives. So instead of
String
use typeCountryCode
UPDATE: Even better: Inline classes in Kotlin. Exists in Java?
- https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.jvm/-jvm-inline/
- https://discuss.kotlinlang.org/t/extension-functions-on-type-aliases-should-only-be-accessable-from-similar-types/3722
- https://kotlinlang.org/docs/inline-classes.html
- https://kotlinlang.org/docs/inline-classes.html#inline-classes-vs-type-aliases
data class CountryAlphaCode( @field:NotNull @field:Pattern(regexp = "[A-Z]{2}", message = "alpha code must be exactly two uppercase letters (A-Z).") val code: String)
data class CountryCode( @field:NotNull val id: UUID,
@field:NotNull val name: String,
@field:NotNull @field:Pattern(regexp = "\\d{3}", message = "numericCode must be exactly three digits.") val numericCode: String,
@field:NotNull @field:Pattern(regexp = "[A-Z]{2}", message = "alphaCode must be exactly two uppercase letters (A-Z).") val alphaCode: String)
data class CountryCode( val id: UUID, val name: String, val numericCode: String, val alphaCode: String) { init { require(numericCode.matches(Regex("\\d{3}"))) { "numericCode must be exactly three digits." } require(alphaCode.matches(Regex("[A-Z]{2}"))) { "alphaCode must be exactly two uppercase letters (A-Z)." } }}
About inline classes / value classes:
A value or inline class (they’re essentially two names for the same concept) is a special type of class designed to wrap a single value without the overhead usually associated with objects. Here are the key points:
- Purpose:
The primary goal is to offer additional type-safety while keeping runtime performance close to using the underlying primitive or value type directly. For example, if you want to prevent mixing up different kinds of IDs (like UserID and ProductID even if both are based on Int), a value/inline class can enforce that at compile time.
- Representation:
At runtime, many usages of a value/inline class might not result in an actual object allocation. Instead, the class is “inlined,” meaning that the representation is reduced to its single underlying field (often a primitive type or another simple type). This inlining minimizes overhead.
- Usage Restrictions:
Since value/inline classes are meant to wrap a single value, they are typically limited to having one property in their primary constructor. There are also restrictions in terms of inheritance and mutability. For example, an inline class cannot have its own state aside from this single property and usually doesn’t support inheritance like regular classes do.
- Benefits: > * Improved performance by avoiding unnecessary object allocation (when inlining is possible).
Enhanced type-safety by distinguishing between semantically different values even if their underlying representation is the same.
- Considerations:
Although the inline mechanism helps with runtime efficiency, the compiler might not always be able to inline the wrapped value, particularly in more complex situations. Also, methods and functions that use inline classes in their signature can be subject to specialized name mangling (which is why changes like adding or removing an annotation can be binary incompatible).
In essence, a value/inline class allows you to encapsulate a single value in a way that helps enforce type-safety and potentially improves performance by eliminating the need to create new objects at runtime.