Статьи

Мы принимаем ставки: эта аннотация скоро появится в JDK

Этот недавний вопрос переполнения стека от Yahor заинтриговал меня: как обеспечить во время компиляции Java 8, что сигнатура метода «реализует» функциональный интерфейс . Это очень хороший вопрос. Давайте предположим следующий номинальный тип:

1
2
3
4
@FunctionalInterface
interface LongHasher {
    int hash(long x);
}

Тип навязывает кристально чистый контракт. Разработчики должны предоставить единственный метод с именем hash() принимающий long аргумент и возвращающий значение типа int . При использовании лямбда-ссылок или ссылок на методы имя метода hash() больше не имеет значения, и будет достаточно структурного типа long -> int .

В своем вопросе Яхор хочет применить приведенный выше тип к трем статическим методам (пример, измененный мной):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
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);
    }
}

И он хотел бы, чтобы компилятор Java жаловался на третий случай, так как randomHash() не «соответствует» LongHasher .

Конечно, ошибку компиляции легко создать, фактически назначив static методы в их функциональных обозначениях (ссылки на LongHasher экземпляру LongHasher :

1
2
3
4
5
6
// OK
LongHasher good = LongHashes::xorHash;
LongHasher alsoGood = LongHashes::continuingHash;
 
// Yikes
LongHasher ouch = LongHashes::randomHash;

Но это не так кратко, как могло бы / должно быть. Ограничение типа должно быть наложено непосредственно на static метод.

И как Java это делает?

С аннотациями, конечно!

Я собираюсь сделать ставку, что следующий шаблон появится в JDK 10:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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);
    }
}

Фактически, вы уже могли реализовать такую ​​аннотацию сегодня и написать свой собственный процессор аннотаций ( или средство проверки JSR-308 ) для проверки этих методов. Ждем еще одну замечательную аннотацию !

Итак, кто готов поспорить, что у нас будет эта аннотация к JDK 10?