Мы можем заменить вызов foo
в bar
вызовом соответствующего BiFunction
.Таким образом, для всех перегруженных foo
методов должен быть определен BiFunction
.
private static final Map<Class<?>, BiFunction<?, Integer, TypeR>> FOO = Map.of(
TypeA.class, (first, second) -> foo((TypeA) first, second),
TypeB.class, (first, second) -> foo((TypeB) first, second),
TypeC.class, (first, second) -> foo((TypeC) first, second));
Но для каждого перегруженного foo
отображения должна быть написана только одна строка, тогда как для каждого перегруженного bar
метода потребуетсячетыре строки, если они написаны как в вопросе.
Теперь мы можем сделать bar
родовым.Но следующее не будет работать, так как возвращенный BiFunction
имеет несвязанный подстановочный знак в качестве параметра первого типа.
<T> TypeR bar(T first, int baz) {
int second = someCalculation(baz);
return FOO.get(first.getClass()).apply(first, second);
}
Нам нужно было бы объявить FOO
как Map<Class<T>, BiFunction<T, Integer, TypeR>>
, но это невозможно.Чтобы обойти это, мы определяем метод с параметром типа T
, который должен установить равенство отсутствующего типа.Но это не бесплатно.Это происходит за счет предупреждения, полученного в результате приведения:
private static <T> BiFunction<T, Integer, TypeR> fooOf(Object o) {
return (BiFunction<T, Integer, TypeR>) FOO.get(o.getClass());
}
Теперь мы можем использовать этот метод в bar
:
<T> TypeR bar(T first, int baz) {
int second = someCalculation(baz);
return fooOf(first).apply(first, second);
}
Это похоже на подход instanceof
, носмещает различие типов от метода bar
к Map
FOO
.Если есть другие методы, кроме bar
, которые также должны вызывать foo
аналогичным образом, различие типов больше не кодируется.FOO
и fooOf
можно использовать повторно.Если библиотека изменяется и вводится дополнительная перегруженная TypeR foo(TypeD first, int second)
, необходимо обновить только FOO
, добавив одну дополнительную строку.
Этот подход требует, чтобы first
не было null
.