Предполагая, что value
является double
, вы можете сделать:
(double)Math.round(value * 100000d) / 100000d
Это для 5-значной точности. Число нулей указывает количество десятичных знаков.
Почему бы не сделать значения аннотации enum, которые являются ключами к фактическим значениям данных, которые вы хотите?
, например
enum InfoKeys
{
A("a"),
B("b"),
AB(new String[] { "a", "b" }),
InfoKeys(Object data) { this.data = data; }
private Object data;
}
@SomeAnnotation (values = InfoKeys.AB)
Это может быть улучшено для безопасности типа, но вы получите эту идею.
Хотя невозможно передать массив непосредственно в качестве значения параметра аннотации, есть способ эффективно получить подобное поведение (в зависимости от того, как вы планируете использовать свои аннотации, это может не работать для каждого варианта использования).
Вот пример - допустим, у нас есть класс InternetServer
и он имеет свойство hostname
. Мы хотели бы использовать регулярную проверку Java, чтобы гарантировать, что ни один объект не имеет «зарезервированное» имя хоста. Мы можем (несколько продуманно) передать массив зарезервированных имен хостов в аннотацию, которая обрабатывает проверку имени хоста.
caveat- с Java Validation, было бы более привычно использовать «полезную нагрузку» для передачи в таком виде данные. Я хотел, чтобы этот пример был более общим, поэтому я использовал собственный класс интерфейса.
// InternetServer.java -- an example class that passes an array as an annotation value
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.Pattern;
public class InternetServer {
// These are reserved names, we don't want anyone naming their InternetServer one of these
private static final String[] RESERVED_NAMES = {
"www", "wwws", "http", "https",
};
public class ReservedHostnames implements ReservedWords {
// We return a constant here but could do a DB lookup, some calculation, or whatever
// and decide what to return at run-time when the annotation is processed.
// Beware: if this method bombs, you're going to get nasty exceptions that will
// kill any threads that try to load any code with annotations that reference this.
@Override public String[] getReservedWords() { return RESERVED_NAMES; }
}
@Pattern(regexp = "[A-Za-z0-9]{3,}", message = "error.hostname.invalid")
@NotReservedWord(reserved=ReservedHostnames.class, message="error.hostname.reserved")
@Getter @Setter private String hostname;
}
// NotReservedWord.java -- the annotation class
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy=ReservedWordValidator.class)
@Documented
public @interface NotReservedWord {
Class<? extends ReservedWords> reserved ();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String message() default "{err.reservedWord}";
}
// ReservedWords.java -- the interface referenced in the annotation class
public interface ReservedWords {
public String[] getReservedWords ();
}
// ReservedWordValidator.java -- implements the validation logic
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ReservedWordValidator implements ConstraintValidator<NotReservedWord, Object> {
private Class<? extends ReservedWords> reserved;
@Override
public void initialize(NotReservedWord constraintAnnotation) {
reserved = constraintAnnotation.reserved();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) return true;
final String[] words = getReservedWords();
for (String word : words) {
if (value.equals(word)) return false;
}
return true;
}
private Map<Class, String[]> cache = new ConcurrentHashMap<>();
private String[] getReservedWords() {
String[] words = cache.get(reserved);
if (words == null) {
try {
words = reserved.newInstance().getReservedWords();
} catch (Exception e) {
throw new IllegalStateException("Error instantiating ReservedWords class ("+reserved.getName()+"): "+e, e);
}
cache.put(reserved, words);
}
return words;
}
}
Это потому, что элементы массива могут быть изменены во время выполнения (Info.AB[0] = "c";
), в то время как значения аннотации являются постоянными после времени компиляции.
Учитывая это, кто-то неизбежно будет смущен, когда они попытаются изменить элемент Info.AB
и ожидаем изменения значения аннотации (это не будет). И если значение аннотации разрешено изменять во время выполнения, оно будет отличаться от значения, используемого во время компиляции. Представьте себе путаницу!
(где путаница здесь означает, что есть ошибка, которую кто-то может находить и тратить часы на отладку.)
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Handler {
enum MessageType { MESSAGE, OBJECT };
String value() default "";
MessageType type() default MessageType.MESSAGE;
}
AB
еще не существует. – Stijn de Witt 7 September 2015 в 19:06private static final String[] AB = { ... };
. понятно, что обработка аннотации происходит до фактической компиляции, но тогда сообщение об ошибке неточно. – asgs 2 May 2018 в 09:16