为什么注释成员不支持静态方法引用类型?

时间:2015-05-01 09:07:52

标签: java annotations

我想创建自己的DateTime验证注释

import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalQuery;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@Constraint(validatedBy = DateTimeValidator.class)
public @interface DateTime {
    String message() default "{com.example.constraints.DateTime.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    TemporalQuery<?>[] queries() default {ZonedDateTime::from, LocalDateTime::from};
}

public class DateTimeValidator implements ConstraintValidator<DateTime, String> {

    private DateTime dateTime;
    private TemporalQuery<?>[] queries;

    @Override
    public void initialize(DateTime dateTime) {
        queries = dateTime.queries();
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        try {
            DateTimeFormatter.ISO_DATE_TIME.parseBest(s, queries);
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }
}

是否有任何理由说明注释类型中不支持静态方法引用?否则,实现目标的最佳途径是什么?

1 个答案:

答案 0 :(得分:1)

简而言之,因为语言禁止它。

Java语言规范普通接口注释类型之间的JLS Chapter 9区分。

虽然普通接口允许标准的 MethodHeader MethodBody 制作,但通过定义注释类型方法的限制性要大得多。具体而言,注释的值实现为虚拟方法,其return types are limited to

  • 基元
  • 字符串
  • 枚举类型
  • 其他注释(除了自身,或那些允许循环路径返回自身的注释)
  • 以上
  • 的数组

因此,根据语言规范,如果您尝试使用任何其他类型,javac必须生成编译时错误。这种语言限制的基本原理来自于注释值如何存储在字节码中所带来的实际限制。

在元素上放置注释时,元素的字节码必须引用值,而不需要访问任何其他类。具体来说,所有注释值实际上都存储在封闭类常量池中。

常量池用于许多目的,但它是一种相对简单的数据结构,并且不希望用它来编码复杂数据。通常条目是类名,方法名,方法签名等。注释参数在池中编码的当前方式建议方法句柄至少需要三(3)个这样的条目对于上述内容,不考虑这些方法是静态,虚拟,构造函数还是来自接口的复杂性。它们涉及仿制药吗?等

你能做什么

如上所述,其中一个允许的类型是任何枚举。如果您希望支持的TemporalQuery实现可以在编译时限制为固定集 ,那么您可以定义enum以包含每个受支持实现的静态成员并将其用作间接层:

public enum TemporalQueryValidation {
    ZONED_DATE_TIME(ZoneDateTime::from),
    LOCAL_DATE_TIME(LocalDateTime::from),
    // ... more ...
    ;

    private final TemporalQuery<?> query;

    private TemporalQueryValidation (TemporalQuery<?> q) {
        this.query = q;
    }

    TemporalQuery<?> getQuery() {
        return query;
    }
}

@Constraint(validatedBy = DateTimeValidator.class)
public @interface DateTime {
    // ... other annotation values ...
    TemporalQueryValidation[] queries() default { ZONED_DATE_TIME, LOCAL_DATE_TIME };
}

public class DateTimeValidator implements ConstraintValidator<DateTime, String> {

    private DateTime dateTime;
    private TemporalQuery<?>[] queries;

    @Override
    public void initialize(DateTime dateTime) {
        TemporalQueryValidation[] validations = dateTime.queries();
        queries = new TemporalQuery<?>[validations.length];
        for (int i = 0; i < queries.length; i++) {
            queries[i] = validations[i].getQuery();
        }
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        try {
            DateTimeFormatter.ISO_DATE_TIME.parseBest(s, queries);
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }
}