Требовать конструктор по умолчанию в java?

Есть ли способ потребовать, чтобы класс использовал конструктор по умолчанию (без параметров), в стороне, используя проверку отражения, как показано ниже: (следующее будет работать, но оно взломано и отражается медленно)

 boolean valid = false;
 for(Constructor<?> c : TParse.class.getConstructors())
 {
   if(c.getParameterTypes().length == 0) {
      valid = true;
      break; 
   }
 }
 if(!valid)
    throw new MissingDefaultConstructorException(...);

Ответ 1

Вы можете построить для этого обработчик аннотации. Обработчики аннотаций - это плагины компилятора, которые запускаются во время компиляции. Их ошибки обнаруживаются как ошибки компилятора и могут даже остановить сборку.

Вот пример кода (я его не запускал):

@SupportedAnnotationTypes("*")   // needed to run on all classes being compiled
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class DefaultConstructor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {

        for (TypeElement type : ElementFilter.typesIn(roundEnv.getRootElements())) {
            if (requiresDefaultConstructor(type))
                checkForDefaultConstructor(type);
        }
        return false;
    }

    private void checkForDefaultConstructor(TypeElement type) {
        for (ExecutableElement cons :
            ElementFilter.constructorsIn(type.getEnclosedElements())) {
            if (cons.getParameters().isEmpty())
                return;
        }

        // Couldn't find any default constructor here
        processingEnv.getMessager().printMessage(
                Diagnostic.Kind.ERROR, "type is missing a default constructor",
                type);
    }

    private boolean requiresDefaultConstructor(TypeElement type) {
        // sample: require any JPA Entity to have a default constructor
        return type.getAnnotation(Entity.class)) != null
               || type.getQualifiedName().toString().contains("POJO");
    }

}

Обработчик аннотации становится еще проще, если вы вводите аннотацию (например, RequiresDefaultAnnotation).

Объявление требования наличия квалификатора по умолчанию

:: Я также предполагаю, что OP запрашивает механизм, предотвращающий случайные ошибки для разработчиков, особенно написанный кем-то другим.::

Должен быть механизм объявления классов, для которых требуется процессор по умолчанию. Надеюсь, у вас уже есть критерии для этого, будь то шаблон в имени, шаблон в квалификаторе, возможная аннотация и/или базовый тип. В приведенном выше примере, вы можете указать критерии в методе requiresDefaultConstructor(). Вот пример того, как это можно сделать:

  • На основе шаблона имени. TypeElement предоставляют доступ к полному имени и имени пакета.

    return type.getQualifiedName().toString().contains("POJO");
    
  • На основе аннотации, содержащейся в объявлении типа. Например, все классы Java Bean Entity должны иметь конструкторы не по умолчанию

    return type.getAnnotation(Entity.class) != null;
    
  • На основе абстрактного класса или интерфейса.

    TypeElement basetype = processingEnv.getElements().getTypeElement("com.notnoop.mybase");
    return processingEnv.getTypes().isSubtype(type.asType(), basetype.asType());
    
  • [Рекомендуемый подход]: Если вы используете интерфейс базового типа, я рекомендую смешивать подход аннотации с интерфейсом базового типа. Вы можете объявить аннотацию, например. MyPlain, вместе с мета-аннотацией: @Inherited. Затем вы можете аннотировать базовый тип с помощью этой аннотации, тогда все подклассы также наследуют аннотацию. Тогда ваш метод будет

    return type.getAnnotation(MyPlain.class) != null;
    

    Это лучше, потому что это немного более настраиваемо, если шаблон действительно основан на иерархии типов, и у вас есть корневой класс.

Как упоминалось ранее, только потому, что он называется "обработкой аннотации", это означает, что вы должны использовать аннотации! Какой подход в списке, который вы хотите использовать, зависит от вашего контекста. В основном, дело в том, что любую логику, которую вы хотели бы настроить в своих инструментах обеспечения развертывания, эта логика идет в requiresDefaultConstructor.

Классы процессора будут работать на

Вызов обработчиков аннотаций в любом классе зависит от SupportedAnnotationTypes. Если мета-аннотация SupportedAnnotationTypes задает конкретную аннотацию, то процессор будет работать только на тех классах, которые содержат такую ​​аннотацию.

Если SupportedAnnotationTypes равно "*", тогда процессор будет вызываться во всех классах, аннотированных или нет! Проверьте [Javadoc] (http://java.sun.com/javase/6/docs/api/javax/annotation/processing/Processor.html#getSupportedAnnotationTypes()), в котором говорится:

Наконец, "*" сам по себе представляет набор всех типов аннотаций, включая пустое множество. Обратите внимание, что процессор не должен требовать "*", если это не фактически обрабатывать все файлы; утверждение ненужных аннотаций может в некоторых случаях сред.

Обратите внимание, как возвращается false, чтобы гарантировать, что процессор не требует всех аннотаций.

Ответ 2

Нет. Вышеупомянутая проверка может быть проще переписана как:

try {
  MyClass.newInstance();
} catch (InstantiationException E) {
  // no constructor
} catch (IllegalAccessException E) {
  // constructor exists but is not accessible
?

Ответ 3

Вы можете использовать PMD и Macker, чтобы гарантировать архитектурные правила. В частности, Macker провоцирует ошибки компиляции, прерывая процесс сборки, когда проверки не выполняются.

Macker расширяет некоторые концепции, популярные PMD относительно валидации исходного кода. Хорошим примером является то, что вы хотите гарантировать, что все классы из пакета реализуют определенный интерфейс.

Итак, если вы очень параноик (как я!) о проверке всех возможных архитектурных правил, Macker действительно полезен.

http://innig.net/macker/

Примечание. Сайт невелик. Цвета повредит вам глаза... но инструменты очень полезны в любом случае.

Ричард Гомес http://www.jquantlib.org/