javaannotationsjava-annotations

Can you get subclass Annotation in parent?


This is wired, but can you do this?

I want use no-argument constructor ( so develop depend on my framework don't need to extend constructor), but I want use final in field. So :

Annotation.java

public @interface Annotation {
    String value();
}

Parent.java

public abstract class Parent {

    public final String name;

    // this will cause you must extends constructor in any sub class
    public Parent(Annotation annotation){
        this.name = annotation.value();
    }
}

Interface define

public abstract class Define extends Parent {


    // You can't do that:
    // Cannot reference 'this' before supertype constructor has been called
    public Define (){
        this(this.getClass().getAnnotation(Annotation.class).value());
    }

    // You can do that, but not working 
    // Define is point at Define as is rather I hope the sub class MyModule
    public Define (){
        this(Define.class.getAnnotation(Annotation.class).value());
    }

    public Define (Annotation annotation){
        super(annotation); // You must extend this
    }

    public abstract void foo();

}

And I hope developer using my framework like this:

public class MyModule extends Define {

    public void foo(){
        // foo bar 
    }

}

But due to the Cannot reference 'this' before supertype constructor has been called, You must write:

@Annotation
public class MyModule extends Define {

    // my framework using scan an IoC auto invoke 
    public MyModule(Annotation annotation){
        super(annotation.value())
    }

    public void foo(){
        // foo bar 
    }

}

The paradox is name is write in annotation, And this must after newInstance. So the question more like:

How to getClass() for sub class?

So the only solution is give up final field and using something like init()?


Solution

  • Instead of requiring each subclass to read the annotation and pass it to the super class, you can move reading the annotation to the base class:

    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation {
      String value() default "defaultValue";
    }
    
    abstract class Base {
      final String value;
      Base() {
        MyAnnotation myAnnotation= getClass().getAnnotation(MyAnnotation.class);
        if (myAnnotation == null) {
          throw new IllegalStateException("Class " + getClass().getName() + " does not have require annotation MyAnnotation");
        }
        this.value = myAnnotation.value();
      }
    }
    
    @MyAnnotation
    class DefaultValue extends Base {
    }
    
    @MyAnnotation("someValue")
    class ExplicitValue extends Base {
    }
    
    class NoAnnotation extends Base {
    }
    

    Given these classes these two lines

    System.out.println(new DefaultValue().value);
    System.out.println(new ExplicitValue().value);
    

    will print defaultValue and someValue respectively. However, this line will throw an IllegalStateException:

    new NoAnnotation();
    

    Unfortunately the Java type system doesn't allow enforcing the requirement for an annotation on each concrete class at compile time, so this runtime exception is the best you can get (outside of other static analysis tools or architecture testing software like ArchUnit).