Как обеспечить на Java 8 время компиляции, когда подпись метода "реализует" функциональный интерфейс
Есть ли в Java 8 любой аналог для ключевого слова implements
для методов?
Скажем, у меня есть функциональный интерфейс:
@FunctionalInterface
interface LongHasher {
int hash(long x);
}
И библиотека из 3 статических методов "реализующая этот функциональный интерфейс:
class LongHashes {
static int xorHash(long x) {
return (int)(x ^ (x >>> 32));
}
static int continuingHash(long x) {
return (int)(x + (x >>> 32));
}
static int randomHash(long x) {
return xorHash(x * 0x5DEECE66DL + 0xBL);
}
}
В будущем я хочу иметь возможность взаимозаменяемо использовать любые ссылки на эти 3 метода в качестве параметра. Например:
static LongHashMap createHashMap(LongHasher hasher) { ... }
...
public static void main(String[] args) {
LongHashMap map = createHashMap(LongHashes::randomHash);
...
}
Как я могу обеспечить во время компиляции, что LongHashes::xorHash
, LongHashes::continuingHash
и LongHashes::randomHash
имеют ту же подпись, что и LongHasher.hash(long x)
?
Ответы
Ответ 1
Нет такой конструкции синтаксиса, о которой вы просите. Однако вы можете создать статическую константу, где вы явно назначаете ссылку на метод для вашего интерфейса:
class LongHashes {
private static final LongHasher XOR_HASH = LongHashes::xorHash;
private static final LongHasher CONTINUING_HASH = LongHashes::continuingHash;
private static final LongHasher RANDOM_HASH = LongHashes::randomHash;
static int xorHash(long x) {
return (int)(x ^ (x >>> 32));
}
static int continuingHash(long x) {
return (int)(x + (x >>> 32));
}
static int randomHash(long x) {
return xorHash(x * 0x5DEECE66DL + 0xBL);
}
}
Таким образом, ваша компиляция будет нарушена, если какая-либо подпись или интерфейс метода несовместимы. Если вы хотите, вы можете объявить их public
и использовать вместо ссылок на методы.
Если вам все равно, что эти статические lambdas будут висящими в памяти во время выполнения, вы можете переместить это объявление в отдельный класс (например, вложенный), который компилируется, но никогда не загружается.
Ответ 2
Я тоже хотел этого, в прошлом, но ты не можешь этого сделать. Но ты знаешь. Была Java до Java 8. Сделайте это вместо:
enum LongHashes implements LongHasher {
XOR {
@Override
public int hash(long x) { ... }
},
CONTINUING {
@Override
public int hash(long x) { ... }
},
RANDOM {
@Override
public int hash(long x) { ... }
}
}
И затем:
public static void main(String[] args) {
LongHashMap map = createHashMap(LongHashes.RANDOM);
...
}
Ответ 3
Вы можете объявлять объекты функции, а не методы.
class LongHashes {
static final LongHasher xorHash = x -> {
return (int)(x ^ (x >>> 32));
};
... etc
LongHashMap map = createHashMap(LongHashes.randomHash);
Ответ 4
Одним из способов было бы вернуть LongHasher
непосредственно из класса LongHashes
:
class LongHashes {
private static int xorHashImpl(long x) {
return (int)(x ^ (x >>> 32));
}
static LongHasher xorHash() {
return LongHashes::xorHashImpl;
}
}
но добавляет некоторый код в ваш класс LongHashes
и связывает его с интерфейсом LongHasher
, что может быть нежелательно (хотя это, по сути, то, о чем вы просите).
Ответ 5
Или просто создайте 3 класса, которые реализуют LongHasher. Когда вам понадобится LongHasher, получите или создайте экземпляр и передайте его:
LongHasher longHasher = ... // new RandomLongHasher(), factory, .......
LongHashMap map = createHashMap(longHasher);
Написание функций как статических методов здесь:
- затрудняет понимание
- звучит как изобретать колесо; колесо является интерфейсом/классами, заново изобретая намек на использование "интерфейса" между кавычками в описании проблемы; -)
Мы не вынуждены везде использовать лямбды.
Ответ 6
Хотя я считаю, что Tagir отвечает на хороший хак, легко забыть добавить частную константу, когда вы создаете новый хешер.
Как обычно, имея дело с потенциальными проблемами рефакторинга, я считаю, что тестирование - это ответ:
public class LongHashesTest {
@Test
public void xorHash() {
LongHasher xorHash = LongHashes::xorHash;
assertEquals(1768181579, xorHash.hash(34312465426524234l));
}
@Test
public void continuingHash() {
LongHasher continuingHash = LongHashes::continuingHash;
assertEquals(1529080340, continuingHash.hash(74543524355l));
}
@Test
public void randomHash() {
LongHasher randomHash = LongHashes::randomHash;
assertEquals(-1100764221, randomHash.hash(4343245345432154353l));
}
}