I have an interface containing a method accepting an object of type java.lang.Class
public interface TypeReviewer {
public int evaluate(Class<?> type);
...
}
Now I want to support the use of objects of this interface in an annotation processor. But, as some classes might not have been compiled during annotation processing, it seems that I need to add a method
public int evaluate(TypeMirror type)
to the interface. (I may need to add a parameter of type javax.lang.model.util.Types as well but that doesn't matter for the upcoming question)
To me it makes no sense to add this method to the interface as it should calculate exactly the same value for javax.lang.model.type.TypeMirror and the TypeMirror basically represents the Class. It is also a possible point of failure as a programmer whose implementing this interface in a class might produce different results in those methods for exactly the same type.
That leads me to my question: What can I do to avoid this kind of redundant implementation for annotation processing and any other meta programming field?
At runtime you will not have access to TypeMirror instances. At compile time you will not have access to all Class instances. Therefore there is no single unified option. The best solution I could think of (which may be appropriate depending on the complexity of your application) is to build your own simplified type API. Here's a rough sketch:
public interface MyType {
public boolean isSubclassOf(MyType otherType);
}
public class MyTypeClassImpl implements MyType {
private Class<?> clazz;
public MyTypeClassImpl(Class<?> clazz) {
this.clazz = clazz;
}
public boolean isSubclassOf(MyType otherType) {
if(otherType instanceof MyTypeClassImpl) {
return clazz.isAssignableFrom(((MyTypeClassImpl)otherType).clazz);
} else {
throw new RuntimeException("TypeMirror encountered in non-annotation-processing environment");
}
}
}
//Similar implementation for type mirrors
public interface TypeFactory {
public MyType fromJavaType(Object type);
}
public class AnnotationProcessorTypeFactory {
private ProcessingEnvironment processingEnv;
public MyType fromJavaType(Object type) {
if(type instanceof TypeMirror) {
return MyTypeMirrorImpl((TypeMirror)type);
} else if (type instanceof Class<?>) {
return MyTypeMirrorImpl(convertToTypeMirror((Class<?>)type));
}
}
private TypeMirror convertToTypeMirror(Class<?> clazz) {
return processingEnv.getElementUtils().getTypeElement(clazz.getCanonincalName());
}
}
public class RuntimeTypeFactory implements TypeFactory {
public MyType fromJavaType(Object type) {
if(!(type instanceof Class<?>)) {
throw new RuntimeException("Illegal use of non-class type in runtime environment");
}
return new MyTypeClassImpl((Class<?>)type);
}
}