Лучшая практика использования флагов в методе Java
Вопрос
Как лучше всего указывать флаги в методе Java?
Я видел, как SWT использовал int в качестве битовых полей, например:
(пример частично из «Эффективной Java, 2-е изд.», стр. 159):
public class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
void printText(String text, int flags) {
}
}
и ваш клиентский вызов выглядит так:
printText("hello", Text.STYLE_BOLD | Text.STYLE_ITALIC);
...но это не рекомендуется, поскольку вы можете смешивать флаги (значения int) из разных классов без каких-либо проверок компилятора.
В той же книге («Эффективная Java») я вижу использование EnumSet, но тогда ваш пользовательский вызов становится:
printText("hello", EnumSet.of(Style.Bold, Style.ITALIC));
Я нахожу это немного многословным и предпочитаю элегантность SWT.
Есть ли другая альтернатива или вам нужно выбрать именно эти два вкуса?
Решение
Угадай, что вы ударили стену.Я не вижу другого варианта.Java Verbose это факт.В таких ситуациях я обычно добавляю локальную переменную, чтобы сделать код более читаемым.Вы можете сделать это,
EnumSet<Style> styles = EnumSet.of(Style.Bold, Style.ITALIC);
printText("hello", styles);
. Другие советы
Если вам нужны флаги битового стиля, Java заключает их в BitSet
.Он существует уже много лет, но мало кто удосуживается его использовать (предпочитая встраивать обработку битов в стиле C в целые числа).
API для BitSet можно найти здесь..
В сочетании с несколькими хорошо выбранными статическими целыми числами он работает очень хорошо, пока вы не начнете проверять и устанавливать несколько битов за один проход.
Я советую вам пойти с EnumSet
подход.
EnumSet<Style> styles = EnumSet.of(Style.Bold, Style.Italic);
Такой подход обеспечивает лучшую безопасность типов и Style
будучи перечислением, он будет иметь полноценные возможности объектно-ориентированного программирования.
Поздний ответ для тех, кто наткнулся на это.Вот один из способов сделать это, чтобы уменьшить память и иметь хороший enum, как API:
public static class MyFlag {
public static final MyFlag A = new MyFlag(1<<0);
public static final MyFlag B = new MyFlag(1<<1);
public static final MyFlag C = new MyFlag(1<<2);
public static final MyFlag ALL = A.and(B).and(C);
private final int flag;
private MyFlag(int flag){
this.flag = flag;
}
public MyFlag and(MyFlag limit){
return new MyFlag(flag & limit.flag);
}
public MyFlag not(MyFlag limit){
return new MyFlag(flag | ~limit.flag);
}
public boolean isSet(MyFlag limit){
if(limit ==null){
return false;
}
return (this.flag & limit.flag) != 0;
}
}
.
Метод:
public void doFoo(MyFlag flag){
if(MyFlag.A.isSet(flag)){
....
}
if(MyFlag.C.isSet(flag)){
....
}
}
.
Вызов:
x.doFoo(MyFlag.A.and(MyFlag.C));
. Если у вас ограниченное количество методов, которые будут принимать набор стилей (например, printText
, в вашем примере), вы можете настроить их подпись, чтобы она принимала переменное количество параметров стиля:
void printText(String text, Style... flags) {
EnumSet<Style> style = logicalOr(flags); // see comment below
...
}
И тогда ваши вызовы очень близки к маршруту с нетипизированным (int) флагом:
printText("hello", Style.BOLD, Style.ITALIC);
К сожалению, нет EnumSet.of(E... )
завод, просто EnumSet.of(E first, E... more)
, поэтому вам понадобится универсальный logicalOr
метод разделения вашего массива на первые + остальные фрагменты. Оставил в качестве упражнения читателю =).