У меня есть существующий 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?

1
user389955 26 Фев 2015 в 09:59

4 ответа

Лучший ответ

Ответь на мой вопрос:

Я думаю, что такой вызов позволит избежать двусмысленности: для 2) foo (a, b, c) и 4) foo (a, b, Boolean), если я вызову таким образом: foo (x, y, Boolean (null)) вместо foo (x, y, null) неоднозначность будет устранена.

0
user389955 1 Апр 2015 в 22:25

Вместо логического флага вы можете использовать Enum как Flag с тремя состояниями.

0
fsch 26 Фев 2015 в 08:50

Если вы используете 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...; }

С тем же успехом это могло быть понятнее.

1
glglgl 26 Фев 2015 в 08:05

А) Вы можете дать новым методам другое имя. fooWithFlag

Б) Вы не могли разрешить null (предположительно, он делает то же самое, что и предыдущие методы без флага), и вместо этого использовать примитив boolean

C) Вы можете создать субинтерфейс ServiceWithFlags или ServiceV2 и поместить новые методы только в него. Новый код может использовать его с помощью нового интерфейса, старый код по-прежнему использует старый интерфейс и не видит методов.

Г) Это только ошибка времени компиляции. Это не сломает уже скомпилированный код. Так что вы можете пойти дальше и добавить свои новые методы. Любая двусмысленность будет уловлена ​​компилятором и легко исправлена ​​в процессе разработки.

1
Thilo 26 Фев 2015 в 07:08