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()?
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).