java需要默认构造函数吗?

Posted

技术标签:

【中文标题】java需要默认构造函数吗?【英文标题】:Require a default constructor in java? 【发布时间】:2009-11-18 22:05:15 【问题描述】:

有没有办法要求一个类有一个默认(无参数)构造函数,除了使用如下的反射检查? (以下方法可行,但它很老套,反射很慢)

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

【问题讨论】:

比“hacky”和慢更糟糕的是它不会导致编译器错误! 遗憾的是,在 Java 中,当您加载不受控制的类时,无法针对这种情况发出编译器错误。 【参考方案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 的返回方式,以确保处理器不会声明所有注释。

【讨论】:

是的,除了需要在类(或构造函数)上放置注释。你将如何执行? :-) 当您提供必须使用的基类或接口时,注解起作用。对于无法在接口中强制使用构造函数的问题,这是一个非常优雅的解决方案。除此之外,您使用 try/catch 的解决方案是我所知道的最优雅的处理方式。尽管这只在运行时有效。 @ChssPly76,几乎正确。他可以创建任何他想确定类是否需要具有默认构造函数的标准。 @notnoop - 你能详细说明一下吗?我将如何使用注释处理器强制特定类具有构造函数,而无需用户首先在其上放置特定注释?我对这里真的很感兴趣——如果能像龙目岛那样做魔法那就太棒了 我也在想同样的事情。在我看来,除非编写类的人知道使用它,否则注释是毫无价值的,在这种情况下,他们可能会知道只编写一个默认构造函数。您不能在基类中定义抽象构造函数(我不认为)【参考方案2】:

没有。上面的检查可以更容易地改写为:

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

【讨论】:

好主意,没想到。如果我决定它实际上值得执行,我可能会这样做。不过,出于我的目的,它并不是真的有必要强制执行,我只是好奇。 我实际上是在为我们使用的某些第 3 方插件强制执行它。上述(以及其他检查)作为部署脚本中验证过程的一部分运行。不过,我很想看看 notnoop 的解决方案是否成功(没有注释依赖)——它肯定会让一些事情变得更容易。 除了这种方法实际上运行构造函数(如果存在)。你可能不想要的副作用【参考方案3】:

您可以使用 PMD 和 Macker 来保证架构规则。 尤其是,Macker 会引发编译错误,从而在验证失败时破坏您的构建过程。

Macker 扩展了 PMD 流行的一些关于源代码验证的概念。一个很好的例子是,当您想保证包中的所有类都实现某个接口时。

所以,如果你非常偏执(像我一样!)验证所有可能的架构规则,Macker 真的很有用。

http://innig.net/macker/

注意:该网站不是很好。颜色会伤害你的眼睛……但无论如何,这些工具非常有用。

理查德·戈麦斯 http://www.jquantlib.org/

【讨论】:

以上是关于java需要默认构造函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

java在子类里调用一个有参的构成函数,会默认调用父类的构造函数吗,为啥???

子类都会首先调用父类的构造函数吗?

创建重载构造函数时,是不是需要默认构造函数? [复制]

从复制构造函数调用默认赋值运算符是不好的形式吗?

在 C# 中,您需要调用基本构造函数吗?

java子类可以继承父类的构造方法吗