У меня есть существующий API перегрузки:
1). foo(a, b) { return foo(a, b, null); }
2). foo(a, b, c) {return foo(a,b,c,null);}
3). foo(a, b, c, d) { implement of the function...; }
Где a, b, c - некоторая структура данных, значение которой также может быть нулевым. теперь мне нужно расширить API с помощью флага. т.е.
4). foo(a, b, Boolean flag)
5). foo(a, b, c, Boolean flag)
6). foo(a, b, c, d, Boolean flag)
Где Boolean может иметь значение null / false / true; существующие API должны по-прежнему существовать для совместимости.
Проблема в следующем: 2) и 4) неоднозначны при вызове foo (a, b, null). то же самое для 3) и 5).
Мой вопрос: как вообще следует расширять новый API?
4 ответа
Ответь на мой вопрос:
Я думаю, что такой вызов позволит избежать двусмысленности: для 2) foo (a, b, c) и 4) foo (a, b, Boolean), если я вызову таким образом: foo (x, y, Boolean (null)) вместо foo (x, y, null) неоднозначность будет устранена.
Вместо логического флага вы можете использовать Enum как Flag с тремя состояниями.
Если вы используете Java 8, вы должны использовать OptionalBoolean вместо Boolean. Он не так хорош, как его компаньоны Optional<>
, OptionalInt
, OptionalDouble
и т. Д., Но в этом случае это может быть реальным преимуществом.
Поскольку упомянутый OptionalBoolean действительно принадлежит com.sun.javafx.scene.control.behavior.OptionalBoolean
, может быть лучшая альтернатива.
Я лично стараюсь воздерживаться от использования null
там, где есть лучшие варианты.
Вместо того
class Foo {
Bar s; // whatever Bar is...
Foo() {
Foo(null)
}
Foo(Bar s) {
this.s = s;
}
public void doStuff() {
if (s != null) {
bar.frobnicate();
}
}
}
Может быть лучше иметь
class Foo {
Bar s; // whatever Bar is...
Foo() {
Foo(Bar.EMPTY_VALUE)
}
Foo(Bar s) {
this.s = s;
}
public void doStuff() {
bar.frobnicate();
}
}
С Bar
, имеющим
public final static EMPTY_VALUE = new Bar() {
// redefine it so that frobnicate() just does nothing.
// This way, EMPTY_VALUE can be used wherever appropriate
}
Это называется шаблоном нулевого объекта. Хотя он не решает напрямую вашу проблему, он разрешает возникшие у вас неоднозначности, используя null
здесь и там.
Другой способ их решения - заставить каждый конструктор напрямую вызывать «главный конструктор».
Таким образом, вы могли бы
foo(a, b) { return foo(a, b, null, null); }
foo(a, c) { return foo(a, null, c, null); }
foo(a, b, c) {return foo(a,b,c,null);}
foo(a, b, d) {return foo(a,b,null, c);}
foo(a, b, c, d) { implement of the function...; }
С тем же успехом это могло быть понятнее.
А) Вы можете дать новым методам другое имя. fooWithFlag
Б) Вы не могли разрешить null
(предположительно, он делает то же самое, что и предыдущие методы без флага), и вместо этого использовать примитив boolean
C) Вы можете создать субинтерфейс ServiceWithFlags
или ServiceV2
и поместить новые методы только в него. Новый код может использовать его с помощью нового интерфейса, старый код по-прежнему использует старый интерфейс и не видит методов.
Г) Это только ошибка времени компиляции. Это не сломает уже скомпилированный код. Так что вы можете пойти дальше и добавить свои новые методы. Любая двусмысленность будет уловлена компилятором и легко исправлена в процессе разработки.
Похожие вопросы
Связанные вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.