What's this?
👋 Hi, I'm P-Y, I work as an Android Eng at Block. I was just looking for an opportunity to try out whtwnd.com, a blog service built on atproto (Bluesky's protocol), to try out the integration between blog, comments & Bluesky feed. 5 minutes ago I wrote a cute piece of Kotlin code, shared it in our Slack, then realized that'd work out just fine as a quick public blog post.
❤️ Enums ❤️
I love enums. I really do! If you've been doing Android for a while, you might have seen my troll project back in 2015: Fragnums ("An enum based library to replace fragments").
So, naturally, I end up creating a lot of enums, and I want to share utility code that operates on those enums.
Let's say we have a Screen
enum:
enum class Screen {
CartScreen,
HomeScreen,
}
See that trailing comma? I also love trailing commas. I love them especially because they make Romain Guy grumpy.
Sorted entries
Because I like neat things, I want to make sure entries are sorted alphabetically. This could be a compiler check, but instead we'll do it with a runtime check from a static initializer, using Enum.entries
(previously Enum.values()
):
enum class Screen {
CartScreen,
HomeScreen,
;
companion object {
init {
val names = entries.map { it.name }
check(names == names.sorted()) {
"Screen enum entries should be sorted alphabetically"
}
}
}
}
Shared order check
I want to sort more enums! Let's move the order check to a shared utility function. We can replace Screen.entries
with enumEntries<T>()
(previously enumValues<T>()
) combined with a reitied type paramater:
object Enums {
inline fun <reified T : Enum<T>> checkEntriesSorted() {
val names = enumEntries<T>().map { it.name }
check(names == names.sorted()) {
"${T::class.java.simpleName} enum entries should be sorted alphabetically"
}
}
}
enum class Screen {
CartScreen,
HomeScreen,
;
companion object {
init {
Enums.checkEntriesSorted<Screen>()
}
}
}
Unique analytics names
Let's add an analytics name to our screens, for logging purposes. We want that name to be unique, so we need a new shared utility: Enums.checkUniqueAnalyticsName<T>()
.
interface HasAnalyticsName {
val analyticsName: String
}
enum class Screen(override val analyticsName: String) :
HasAnalyticsName {
CartScreen("Cart"),
HomeScreen("Home"),
;
companion object {
init {
Enums.checkEntriesSorted<Screen>()
Enums.checkUniqueAnalyticsName<Screen>()
}
}
}
Where it's at
How can we implement Enums.checkUniqueAnalyticsName<T>()
? We need a function that operates on enums (so that we can call enumEntries<T>()
) but we also need to pull out analyticsName
from each individual entry.
So we want to constrain T
to extend Enum<T>
and also implement HasAnalyticsName
. In Kotlin we can do that with the where
keyword:
inline fun <reified T> checkUniqueAnalyticsName()
where T : Enum<T>,
T : HasAnalyticsName {
val names = enumValues<T>().map { it.analyticsName }
check(names == names.distinct()) {
"${T::class.java.simpleName} enum entries should have a unique analytics name"
}
}
Isn't this neat?
Fin
Comments welcome! I want to see if the integration with Bluesky makes this more fun than with other blogging platforms.