0

I'm reviewing how reflection works or possible work. I have this SomeClassBuilder wherein it has an attribute target : Target with declared annotation TargetAnnotation.

Thing is, is it possible to override/update the values/properties of Target wherein upon invoke of someMethod() would return the parameters on the annotation?

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TargetAnnotation {
    String first();
    String second();
    // other attributes
}

public class Target {
    String first;
    String second;
    // some other attributes unique only to `Target`
}

public interface TargetHelper {
    void setTarget(Target target);
}

public class SomeClassBuilder implements TargetHelper {

    @TargetAnnotation(first = "first", second = "second")
    private Target target;

    @Override public void setTarget(Target target) { this.target = target }

    public void someMethod() {
        System.out.println(target.first); // should be `first`
        System.out.println(target.second); // should be `second`
    }

}

Or is it even possible to do it without TargetHelper interface?

Let's say I have this TargetProcessor called before SomeClassBuilder which sole purpose is to fill-in the target : Target annotated with @TargetAnnotation and assign the field/attributes from @TargetAnnotaton to Target.

public class TargetProcessor {
    public void parse() {
        // look into `@TargetAnnotation`
        // map `@TargetAnnotation` properties to `Target`
    }
}
David B
  • 3,269
  • 12
  • 48
  • 80

2 Answers2

0

You can achieve this by implementing Annotation Processor for your annotation @TargetAnnotation

For further readings and examples:

  1. http://www.baeldung.com/java-annotation-processing-builder
  2. https://github.com/bozaro/example-annotation-processor/blob/master/example-modify/processor/src/main/java/ru/bozaro/processor/HelloProcessor.java
  3. https://deors.wordpress.com/2011/10/08/annotation-processors/

This article explains, how it should be done:

  1. http://hannesdorfmann.com/annotation-processing/annotationprocessing101
Yogi
  • 1,805
  • 13
  • 24
  • Maven is used for compilation here. Below example you can follow for without maven compilation too. https://www.javacodegeeks.com/2015/09/java-annotation-processors.html – Yogi Oct 16 '17 at 08:44
  • Check out the article i have mentioned as last link – Yogi Oct 16 '17 at 08:46
  • Still not a solution for me since I can't invoke any classes of `AbstractProcessor` – David B Oct 16 '17 at 09:12
  • You don't need to invoke any class, but you need to provide while compile like _-processor YourAnnotationProcessor class_ – Yogi Oct 16 '17 at 10:01
0

Here is my code

import static xdean.jex.util.lang.ExceptionUtil.uncheck;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.util.stream.Stream;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import xdean.jex.util.reflect.ReflectUtil;

public class Q46765735 {

  public static void main(String[] args) {
    create(TargetDomain.class).printTarget();
  }

  public static <T> T create(Class<T> clz) {
    T target = uncheck(() -> clz.newInstance());
    Stream.of(ReflectUtil.getAllFields(clz, false)).forEach(f -> uncheck(() -> fill(target, f)));
    return target;
  }

  private static <T> void fill(T target, Field field) throws Exception {
    TargetAnnotation anno = field.getAnnotation(TargetAnnotation.class);
    if (anno == null) {
      return;
    }
    Class<?> type = field.getType();
    if (!Target.class.isAssignableFrom(type)) {
      return;
    }
    field.setAccessible(true);
    Target value = (Target) field.get(target);
    if (value == null) {
      value = (Target) type.newInstance();
    }
    value.setFirst(anno.first());
    value.setSecond(anno.second());
    field.set(target, value);
  }
}

@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target({ ElementType.FIELD })
@interface TargetAnnotation {
  String first();

  String second();
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
class Target {
  String first;
  String second;
}

class TargetDomain {

  @TargetAnnotation(first = "first", second = "second")
  private Target target = new Target("a", "b");

  public void printTarget() {
    System.out.println(target.first); // should be `first`
    System.out.println(target.second); // should be `second`
  }
}


Tips:

  1. You can replace lombok by write constructor and getter/setter manually.
  2. ReflectUtil.getAllFields get all fields of the class.
  3. uncheck simply ignore exceptions, you can use try-catch.
Dean Xu
  • 4,438
  • 1
  • 17
  • 44