vendredi 26 juin 2020

How to pass different annotations with the same property as a method parameter and have the ability to use that property?

CASE

I have a class whose responsibility is to compare field by field two instances of a DTO class for a certain subset of all DTO instance fields and collect "pretty" names of the fields whose values are different. Other fields may be added in the future and they may or may not be included in this comparison.

To allow for this expansion, this is currently implemented as follows: a field that needs to be included in the comparison logic is annotated with a custom annotation and its pretty name is passed as the annotation parameter.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ComparableField {
  String prettyName();
}

public class Dto {
  private String field1; //not included in the comparison logic

  @ComparableField(prettyName="Pretty field 2")
  private String field2;

  @ComparableField(prettyName="Pretty field 3")
  private String field3;
}

The aforementioned class uses reflection to iterate through all the fields of the DTO class and check if that annotation is present, compares the values, and, if different, adds the pretty name to a set.

public Set<String> getAnnotatedDifferentFields(Dto dto1, Dto dto2) {
  Set<String> result = new HashSet<>();
  Field[] declaredFields = dto1.getClass().getDeclaredFields();
  Arrays.stream(declaredFields)
      .filter(field -> field.isAnnotationPresent(ComparableField.class))
      .forEach(field -> {
          field.setAccessible(true);
          if (!field.get(dto1).equals(field.get(dto2)) {
            result.add(field.getAnnotation(ComparableField.class).prettyName());
          }
      });

  return result;
}

I missed exception handling and other complications on purpose.

PROBLEM

There's a new requirement now: all "comparable" fields will have some logical groupings and the resulting sets should be different as well.

QUESTION

Is there an elegant way to implement this requirement along the same lines? I was thinking having different annotations for different logical groups and calling the method with the required annotation as a method parameter:

public Set<String> getAnnotatedDifferentFields(Dto dto1, Dto dto2, Class<? extends Annotation> annotationClass) {
  Set<String> result = new HashSet<>();
  Field[] declaredFields = dto1.getClass().getDeclaredFields();
  Arrays.stream(declaredFields)
      .filter(field -> field.isAnnotationPresent(annotationClass))
      .forEach(field -> {
          field.setAccessible(true);
          if (!field.get(dto1).equals(field.get(dto2)) {
            result.add(field.getAnnotation(annotationClass).prettyName()); //compile error here
          }
      });

  return result;
}

But this of course gives me a compile error because annotationClass is now generic and might not have the prettyName property. There's also no such thing as annotation inheritance, wherebe I would create a parent annotation with the property and several child annotations. I also thought about annotating my custom annotations with another annotation but that wouldn't work either.





Aucun commentaire:

Enregistrer un commentaire