Есть ли способ избежать этой ошибки «значение атрибута должно быть постоянным»?

У нас есть созданная нами аннотация, которая используется для проверки ввода в метод. Что-то вроде этого:

@InputValidation(paramName = "foo", regularExpression = RegularExpressionConstants.FOO_REG_EX)

В нашем классе RegularExpressionConstants есть множество различных строковых констант, представляющих разные регулярные выражения. Этот класс начал выглядеть очень запутанным, поэтому я начал пытаться привести его в порядок с помощью методов, которые немного облегчили его чтение и т. д. Итак, теперь класс выглядит так:

public static final String FOO_REG_EX = alphanumericWithRange(1, 16);
public static final String BAR_REG_EX = alphanumericWithRange(2,4);

private static String alphanumericWithRange(int lowerLimit, int upperLimit) {
    "[a-zA-Z0-9]{" + lowerLimit + "," + upperLimit + "}";
}

Класс RegularExpressionConstants компилируется, но аннотации больше не компилируются. Ошибка Attribute value must be constant. Посмотрев на некоторые связанные вопросы на StackOverflow, я понимаю, почему это происходит. Мне в основном интересно, есть ли способ добиться желаемой аккуратности в классе констант, не вызывая этой проблемы? Или мне просто придется иметь дело с множеством беспорядочных констант?

Вы уже назвали константы (учитывая, что на самом деле вы используете более удачные имена, чем FOO_REG_EX и BAR_REG_EX), поэтому нет необходимости в дополнительной «аккуратности», используя, например, alphanumericWithRange(1, 16) вместо "[a-zA-Z0-9]{1,16}" или "\\p{Alnum}{1,16}". Использование дополнительного метода здесь слишком сложно.

Holger 10.09.2018 11:03

@Holger, дело не только в опрятности. Кроме того, когда другой разработчик добавляет регулярное выражение и знает, что его регулярное выражение должно быть буквенно-цифровым, он может вызвать существующий метод, не беспокоясь о написании совершенно нового регулярного выражения. Снижает риск ошибок и экономит время будущих разработчиков.

Ben Green 10.09.2018 11:15
1
2
4 097
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать перечисление вместо списка строковых констант:

@interface InputValidation {
    RegularExpressionConstants regularExpression() default 
             RegularExpressionConstants.FOO_REG_EX;
}

Использование перечисления позволяет также перемещать метаданные name, в которых определен шаблон.

enum RegularExpressionConstants {
    FOO_REG_EX("foo", alphanumericWithRange(1, 16)), 
    BAR_REG_EX("bar", alphanumericWithRange(2,4));

    private final String name;
    private final String pattern;

    private RegularExpression(String name, String pattern) {
        this.name = name;
        this.pattern = pattern;
    }

    public String getName() {
        return name;
    }

    public String getPattern() {
        return pattern;
    }

    private static String alphanumericWithRange(int lowerLimit, int upperLimit) {
        return "[a-zA-Z0-9]{" + lowerLimit + "," + upperLimit + "}";
    }
}

А аннотацию можно применить с помощью перечисления:

@InputValidation(regularExpression=RegularExpressionConstants.FOO_REG_EX)

Там, где аннотация обрабатывается, достаточно просто вызвать:

String pattern = field.getAnnotation(InputValidation.class)
    .regularExpression()
    .getPattern();

Я не понимаю, чем это сейчас полезно? OP ожидает regex (String) для аннотации, и теперь он получает enum; Я не думаю, что это вариант полностью рефакторинг

Eugene 10.09.2018 10:47

@ Евгений Я просто добавлял это. Помимо решения проблемы, enum - лучшая альтернатива строковым константам.

ernest_k 10.09.2018 10:51

Это хорошая идея. Если я изменю свой код, который использует аннотацию, чтобы ожидать перечисление вместо строки, тогда это определенный вариант. Я попробую, а затем, как только я убедился, что он работает, я вернусь и приму ваш ответ :)

Ben Green 10.09.2018 10:51

@ernest_k, очевидно, ОП хочет изменить свой код, я ошибался, и это хорошо. 1+

Eugene 10.09.2018 10:54

Если вы не выполните предложение Эрнеста и не проведете рефакторинг кода, вы просто не сможете. Недостающая часть в вашем случае - это принудительное исполнение JSL (для чего является постоянная времени компиляции)

initialized with a constant expression

а вызвать метод просто нет; даже если вы можете сказать, что это константа, компилятор не может и не будет.

Другие вопросы по теме