标题中的问题是“如何强制在我的抽象类的所有子类中定义构造函数”,我想回答这个问题,而不是讨论楼主的真实需求。
答案是:
你可以选择使用APT处理器。
@SupportedAnnotationTypes("pl.selvin.constructorsconstraints.apt.ConstructorConstraint")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ConstructorConstraintProcessor extends AbstractProcessor {
private static final TypeVisitor<Boolean, ArrayList<String>> constraintArgsVisitor =
new SimpleTypeVisitor7<Boolean, ArrayList<String>>() {
public Boolean visitExecutable(ExecutableType t, ArrayList<String> args) {
final List<? extends TypeMirror> types = t.getParameterTypes();
if (args.size() != types.size()) {
return false;
}
for (int i = 0; i < args.size(); i++) {
if (!args.get(i).equals(types.get(i).toString()))
return false;
}
return true;
}
};
@Override
public Set<String> getSupportedOptions() {
final HashSet<String> ret = new HashSet<>();
ret.add("org.gradle.annotation.processing.aggregating");
return ret;
}
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
for (final TypeElement type : annotations) {
processConstructorConstraintClasses(env, type);
}
return true;
}
private void processConstructorConstraintClasses(final RoundEnvironment env, final TypeElement type) {
final Element constructorConstraintElement = processingEnv.getElementUtils().getTypeElement(ConstructorConstraint.class.getName());
final TypeMirror constructorConstraintType = constructorConstraintElement.asType();
final HashMap<String, ArrayList<String>> constructorConstraints = new HashMap<>();
final ArrayList<Element> elements = new ArrayList<>();
for (final Element element : env.getElementsAnnotatedWith(type)) {
elements.add(element);
for (AnnotationMirror am : element.getAnnotationMirrors()) {
if (am.getAnnotationType().equals(constructorConstraintType)) {
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) {
if ("arguments".equals(entry.getKey().getSimpleName().toString()) && entry.getValue() instanceof Attribute.Array) {
final Attribute.Array array = (Attribute.Array) entry.getValue();
for (final Attribute a : array.values) {
final String className = element.toString();
final ArrayList<String> arguments;
if(constructorConstraints.containsKey(className)) {
arguments = constructorConstraints.get(className);
} else {
arguments = new ArrayList<>();
constructorConstraints.put(className, arguments);
}
arguments.add(a.getValue().toString());
}
}
}
break;
}
}
}
for (Element element : elements) {
final TypeMirror derived = element.asType();
for (String className : constructorConstraints.keySet()) {
final TypeMirror baseType = processingEnv.getElementUtils().getTypeElement(className).asType();
if(derived.equals(baseType)) {
continue;
}
if(processingEnv.getTypeUtils().isAssignable(derived, baseType)) {
processClass(element, constructorConstraints.get(className));
}
}
}
}
private void processClass(Element element, ArrayList<String> arguments) {
if (!doesClassContainConstructorWithConstraint(element, arguments)) {
final String needs;
if (arguments == null || arguments.size() == 0) {
needs = "a No-Args Constructor";
} else {
needs = "a Contrcutor with arguments(" + String.join(", ", arguments) + ")";
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Class " + element + " needs " + needs);
}
}
private boolean doesClassContainConstructorWithConstraint(Element element, ArrayList<String> arguments) {
for (final Element subElement : element.getEnclosedElements()) {
if (subElement.getKind() == ElementKind.CONSTRUCTOR && subElement.getModifiers().contains(Modifier.PUBLIC)) {
final TypeMirror mirror = subElement.asType();
if (mirror.accept(constraintArgsVisitor, arguments))
return true;
}
}
return false;
}
}
优点:
- 如果派生类没有这样的构造函数,它将在编译时报错。
缺点:
Github页面:
https://github.com/SelvinPL/ConstructorsConstraints
- 修复了多个基类注释导致的奇怪行为
FX我想使用类似的东西
public class QueryProviders {
private final ArrayList<AbstractQueryProvider> providers = new ArrayList<>();
@SafeVarargs
public QueryProviders(SomeClass arg1, ...OtherArgs... ,
Class<? extends AbstractQueryProvider>... providerClasses) {
try {
for (Class<? extends AbstractQueryProvider> providerClass : providerClasses) {
final Constructor<? extends AbstractQueryProvider> ctor = providerClass.getConstructor(SomeClass arg1, ...OtherArgs...);
providers.add(ctor.newInstance(arg1, ...OtherArgs...));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
和班级
public abstract class AbstractQueryProvider {
public AbstractQueryProvider(SomeClass arg1, ...OtherArgs...) {
}
}
和使用
QueryProviders providers = new QueryProviders(SomeClass arg1, ...OtherArgs...,
Backup.class,
GoodsPrices.class, ...OtherDerived...);
其中Backup
和GoodsPrices
是从AbstractQueryProvider
派生出来的。
当我忘记这样的构造函数时,我需要在编译时出现错误(换句话说,“强制一个构造函数”)
因此,使用APT处理器,您将拥有以下类:
@ConstructorConstraint(SomeClass.class, ...)
public abstract class AbstractQueryProvider {
public AbstractQueryProvider(SomeClass arg1, ...) {
}
}
(包: "pl.selvin.constructorsconstraints.apt")
带有这样的注释:
@Inherited
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface ConstructorConstraint {
Class<?>[] arguments() default {};
}
visitExecutable
以检查不是t.getParameterTypes().isEmpty()
而是如果给定类的参数(您可能可以将其传递给注释)...它将在编译时工作。 - Selvin