Map key limit and value types - harder

I have a more difficult problem (than the question "Java map with values ​​limited by the key type parameter") for displaying the key and the type of value on the map. Here it is:

interface AnnotatedFieldValidator<A extends Annotation> {
  void validate(Field f, A annotation, Object target);
  Class<A> getSupportedAnnotationClass();
}

Now I want to store validators on the map so that I can write the following method:

validate(Object o) {
  Field[] fields = getAllFields(o.getClass());
  for (Field field: fields) {
    for (Annotation a: field.getAnnotations()) {
      AnnotatedFieldValidator validator = validators.get(a);
      if (validator != null) {
        validator.validate(field, a, target);
      }
    }
  }
}

(type parameters are omitted here, since I have no solution). I also want to be able to register my validators:

public void addValidator(AnnotatedFieldValidator<? extends Annotation> v) {
  validators.put(v.getSupportedAnnotatedClass(), v);
}

Using this (only) public modifier method, I can guarantee that the map contains entries for which the key (annotation class) matches the annotation class supported by the validator.

Here is an attempt:

I declare Map validators as follows:

private Map<Class<? extends Annotation>, AnnotatedFieldValidator<? extends Annotation>> validators;

, ( - addValidator()), :

for (Annotation a: field.getAnnotations()) {
  AnnotatedFieldValidator<? extends Annotation> validator = validators.get(a);
  if (validator != null) {
    validator.validate(field, validator.getSupportedAnnotationClass().cast(a), target);
  }
}

: The method validate(Field, capture#8-of ?, Object) in the type AnnotatedFieldValidator<capture#8-of ?> is not applicable for the arguments (Field, capture#9-of ?, Object).

, : AnnotatedFieldValidator (A), getSupportedAnnotationClass(), validate(); , AnnotationClass validate(). getSupportedAnnotationClass() , validate()?

validate(), validate(), , , addValidator() .

+3
5

, .

flicken : . validators.get() . , ( , map):

public void validate(Object o) {
  Field[] fields = getFields(o.getClass());
  for (Field field : fields) {
    Annotation[] annotations = field.getAnnotations();
    for (Annotation annotation : annotations) {
      AnnotatedFieldValidator<? extends Annotation> validator = 
          validators.get(annotation.annotationType());
      if (validator != null) {
        doValidate(field, validator, annotation, o);
      }
    }
  }
}

doValidate() :

private <A extends Annotation> void doValidate(Field field, 
      AnnotatedFieldValidator<A> validator, Annotation a, Object o) {
    // I assume this is correct following only access to validators Map
    // through addValidator()
    A annotation = validator.getSupportedAnnotationClass().cast(a);
    try {
        validator.validate(field, annotation, bean, beanName);
    } catch (IllegalAccessException e) {
    }
}

(, Class.cast()...), , , .

0

. validators , , .

    protected <A extends Annotation> AnnotatedFieldValidator<A> getValidator(A a) {
        // unchecked cast, but isolated in method
        return (AnnotatedFieldValidator<A>) validators.get(a);
    }

    public void validate(Object o) {
        Object target = null;
        Field[] fields = getAllFields(o.getClass());
        for (Field field : fields) {
            for (Annotation a : field.getAnnotations()) {
                AnnotatedFieldValidator<Annotation> validator = getValidator(a);
                if (validator != null) {
                    validator.validate(field, a, target);
                }
            }
        }
    }

    // Generic map
    private Map<Class<? extends Annotation>, AnnotatedFieldValidator<? extends Annotation>> validators;

( .)

+1

, ; . generics, . ,

List<String> list = new ArrayList<String>();
list.add("test");
String s = list.get(0);

List list = new ArrayList();
list.add("test");
String s = (String) list.get(0);

.

, generics . .

validator.validate(field, a, target);

, Annotation .

, A :

interface AnnotatedFieldValidator {
  void validate(Field f, Annotation annotation, Object target);
  Class<? extends Annotation> getSupportedAnnotationClass();
}

addValidator , ..

public void addValidator(AnnotatedFieldValidator v) {
  validators.put(v.getSupportedAnnotationClass(), v);
}

, , , . , , .

if (validator.getSupportedAnnotationClass().isInstance(a)) {
  validator.validate(field, a, target);
}
else {
  // wrong type of annotation, throw some exception.
}
0

Haa.. , :)

for (Annotation a: field.getAnnotations()) {
  AnnotatedFieldValidator<? extends Annotation> validator = validators.get(a);
  if (validator != null) {
    validator.validate(field, validator.getSupportedAnnotationClass().cast(a), target);
  }
}

EDIT: ( )

, , .

, validator.getSupportedAnnotationClass().cast() Class<? extends Annotation>.

:

validator.validate(field, validator.getSupportedAnnotationClass().cast(a), target);

Class<? extends Annotation>.

. 2 , .

0

, , - , - . , , , .

. ( AnnotatedFieldValidator AFV)

interface AFV<A extends Annotation>
{
  void validate(Field f, A annotation, Object target);
  Class<A> getSupportedAnnotationClass();
}

private Map<Class<? extends Annotation>, AFV<? extends Annotation>> validators;

public void validate(Object o) {
  Field[] fields = o.getClass().getDeclaredFields();
  for (Field field: fields) {
    for (Annotation a: field.getAnnotations()) {
      AFV<? extends Annotation> validator = validators.get(a.getClass());
      if (validator != null) {
        validator.validate(field, a, o);
      }
    }
  }
}

public void addValidator(AFV<? extends Annotation> v) {
  validators.put(v.getSupportedAnnotationClass(), v);
}

, , , , Annotation, . AFV Map, validate , , AFV Annotation , Annotation, .

As you said to yourself, if the only way to add validators is addValidatorthrough a method , then you can internally provide type safety through it and rewrite it as follows:

interface AFV<A extends Annotation>
{
    void validate(Field f, A annotation, Object target);
    Class<A> getSupportedAnnotationClass();
}

private Map<Class<?>, AFV<?>> validators;

public void validate(Object o)
{
    Field[] fields = o.getClass().getDeclaredFields();
    for (Field field : fields)
    {
        for (Annotation a : field.getAnnotations())
        {
            // raw type to keep compiler happy
            AFV validator = validators.get(a.getClass());
            if (validator != null)
            {
                validator.validate(field, a, o); // statically unsafe
            }
        }
    }
}

public void addValidator(AFV<?> v)
{
    validators.put(v.getSupportedAnnotationClass(), v);
}

Note that the call is validator.validatenow statically unsafe, and the compiler will issue a warning. But if you can live with it, it can do.

0
source

Source: https://habr.com/ru/post/1702748/


All Articles