Лучшая практика использования флагов в методе Java

StackOverflow https://stackoverflow.com/questions/6073347

  •  07-09-2020
  •  | 
  •  

Вопрос

Как лучше всего указывать флаги в методе 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 метод разделения вашего массива на первые + остальные фрагменты. Оставил в качестве упражнения читателю =).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top