DEV Community


Posted on • Originally published at

How I blundered with enum's hashCode

I was implementing an HTTP caching for one of our APIs, based on "ETag" and "If-None-Match" headers. The entity I was trying to cache — Feed — was simple:

data class Feed(
    val deviceId: String,
    val platform: Platform,
    val items: List<FeedItem>

enum class Platform {

data class FeedItem(
    val id: Int
    // Integers and strings
Enter fullscreen mode Exit fullscreen mode

I decided to use hashCode() for ETags.

I knew that both Integer and String have a well-defined and stable hash code: Integer#hashCode() is equal to the primitive value represented by that Integer object, and String#hashCode() uses a known formula. List#hashCode() is stable as well as long as it’s elements have stable hash codes.

yeah, we happy

Of course not, we’re not happy! I forgot to check the details about Enum#hashCode() method.

And here be dragons: enum’s hash code is not stable across different JVM instances. I assumed wrongly that it is calculated as value’s ordinal(), which is Integer, which, we know, has a stable hash code, equal to itself. But it’s not. Enum’s hash code is defined in Java (Kotlin is virtually the same) as:

public final int hashCode() {
    return super.hashCode();
Enter fullscreen mode Exit fullscreen mode

Here, super is an Object, and, as we know, Object's hash code is stable unless the object itself is changed. It’s generally the case with enums: there is only one instance of each of enum’s values per JVM and they are stable. But it’s important to emphasize, that it applies to a single JVM. Enum’s on different JVMs will have different hash codes.

And that’s how our ETags were broken: every time the request landed on a new server, the ETag was different, even if the objects stored in the database were the same. The situation was worsened by the fact that we were using short-lived JVMs (AWS Lambda functions), so the caching was completely broken for a while until I tracked down this issue.


Discussion (0)