We’re Taking Bets: This Annotation Will Soon Show up in the JDK

This recent Stack Overflow question by Yahor has intrigued me: How to ensure at Java 8 compile time that a method signature “implements” a functional interface. It’s a very good question. Let’s assume the following nominal type:

@FunctionalInterface
interface LongHasher {
    int hash(long x);
}

The type imposes a crystal clear contract. Implementors must provide a single method named hash() taking a long argument, returning a int value. When using lambdas or method references, then the hash() method name is no longer relevant, and the structural type long -> int will be sufficient.

In his question, Yahor wants to enforce the above type upon three static methods (example modified by me):

class LongHashes {

    // OK
    static int xorHash(long x) {
        return (int)(x ^ (x >>> 32));
    }

    // OK
    static int continuingHash(long x) {
        return (int)(x + (x >>> 32));
    }

    // Yikes
    static int randomHash(NotLong x) {
         return xorHash(x * 0x5DEECE66DL + 0xBL);
    }
}

And he would like the Java compiler to complain in the third case, as the randomHash() does not “conform” to LongHasher.

A compilation error is easy to produce, of course, by actually assigning the static methods in their functional notation (method references) to a LongHasher instance:

// OK
LongHasher good = LongHashes::xorHash;
LongHasher alsoGood = LongHashes::continuingHash;

// Yikes
LongHasher ouch = LongHashes::randomHash;

But that’s not as concise as it could / should be. The type constraint should be imposed directly on the static method.

And what’s the Java way of doing that?

With annotations, of course!

I’m going to take bets that the following pattern will show up by JDK 10:

class LongHashes {

    // Compiles
    @ReferenceableAs(LongHasher.class)
    static int xorHash(long x) {
        return (int)(x ^ (x >>> 32));
    }

    // Compiles
    @ReferenceableAs(LongHasher.class)
    static int continuingHash(long x) {
        return (int)(x + (x >>> 32));
    }

    // Doesn't compile
    @ReferenceableAs(LongHasher.class)
    static int randomHash(NotLong x) {
         return xorHash(x * 0x5DEECE66DL + 0xBL);
    }
}

In fact, you could already implement such an annotation today, and write your own annotation processor (or JSR-308 checker) to validate these methods. Looking forward to yet another great annotation!

So, who’s in for the bet that we’ll have this annotation by JDK 10?

6 thoughts on “We’re Taking Bets: This Annotation Will Soon Show up in the JDK

    • That’s a pragmatic workaround, of course, but the method would now have very different semantics. It would become a higher-kinded function (as much as a static method can be considered a function in the first place), just because of a workaround. Not necessarily what you want to do…

  1. Highly doubtful that Java would supply an annotation that binds to a class due to creating a compilation dependency, and god forbid not a string version. The compiler will catch the error at the call site and unit tests would be a reasonable expectation. Most likely the JDK leads would give a pragmatic shrug as they do to most warts in the language.

      • An annotation that takes a class argument creates a new compile time dependency. @Override enforces an expectation of an existing dependency defined through inheritance. The proposed annotation is closer to C++’s friend class than static analysis annotations provided by Java and the JSRs.

        @ReferenceableAs creates a compilation dependency that does not exist until the call site transforms it into a SAM type. This is akin to having an interface know of its implementation by tightly coupling the two together. This is okay for projects where the code is all within one compilation unit, but doesn’t make sense across modules / external libraries. It might be nice to add the metadata to Java core types for an application, but that won’t happen obviously.

        The only way to have the annotation work without creating a compile time dependency, but allowing static analysis to detect it, would be using strings. So while the idea has merit as nice to have, I’d expect it to be too problematic for the Java core team to consider.

        • Well, when you implement a Runnable, you’re creating a compile-time dependency. Apparantly, of a “good” kind. When you “structurally implement” a Runnable via a static method whose method reference is assignment compatible with a Runnable, and thus could be annotated with @ReferenceableAs, then that sort of compile-time dependency isn’t of a “good” kind anymore?

          Perhaps, you’re painting black the (ab-)usage of such an annotation: “when all you have is a hammer, every problem starts looking like a thumb” 😉

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s