隐藏实用程序类构造函数:实用程序类不应具有公共或默认构造函数

Posted

技术标签:

【中文标题】隐藏实用程序类构造函数:实用程序类不应具有公共或默认构造函数【英文标题】:Hide Utility Class Constructor : Utility classes should not have a public or default constructor 【发布时间】:2013-01-02 03:40:18 【问题描述】:

我在 Sonar 上收到此警告。我想要解决方案来删除 Sonar Qube 上的此警告。

我的课是这样的:

public class FilePathHelper 
    private static String resourcesPath;
    public static String getFilePath(HttpServletRequest request) 
        if(resourcesPath == null) 
            String serverpath = request.getSession().getServletContext().getRealPath("");               
            resourcesPath = serverpath + "/WEB-INF/classes/";   
        
        return resourcesPath;       
    

我需要适当的解决方案来消除声纳上的此警告。

【问题讨论】:

最好禁用这个无用的声纳警告。私有构造函数添加代码噪音只是为了让任意 Sonar 规则满意。在这里查看如何禁用规则:sqa.stackexchange.com/questions/24734/… 【参考方案1】:

我不知道 Sonar,但我怀疑它正在寻找私有构造函数:

private FilePathHelper() 
    // No-op; won't be called

否则,Java 编译器将提供一个公共的无参数构造函数,这是您不想要的。

(您还应该将类设为 final,尽管其他类由于它只有一个私有构造函数而无法扩展它。)

【讨论】:

【参考方案2】:

如果这个类只是一个实用类,你应该把这个类定为final并定义一个私有构造函数:

public final class FilePathHelper 
   private FilePathHelper() 
      //not called
   

这可以防止默认的无参数构造函数在代码的其他地方使用。

此外,您可以将类设为 final,这样它就不能在子类中扩展,这是实用程序类的最佳实践。由于您仅声明了一个私有构造函数,因此其他类无论如何都无法扩展它,但将类标记为 final 仍然是最佳实践。

【讨论】:

final 实际上是多余的:一个只有私有构造函数的类实际上已经是最终的。不过,标记它并没有什么坏处。 必须将该类标记为final,Sonar 才能真正认为违规已解决。仅添加私有构造函数并不能清除违规。 我需要为此启用一些声纳规则吗?我已经创建了我的实用程序类 final 并有一个私有构造函数。构造函数现在被标记为“未覆盖线” 确实,您应该如何测试/覆盖这种“无法访问”的代码? 所以我们应该在我们的类中添加不必要的代码,只是为了让一些分析工具满意?有人可以扩展或实例化这个类。所以呢?为什么这很关键?会导致一些错误吗?或者我为什么要使用 PowerMock 之类的工具来测试从未使用过的私有构造函数,只是为了满足 Sonar 和我的代码覆盖工具的要求?【参考方案3】:

我使用没有实例的枚举

public enum MyUtils  
    ; // no instances
    // class is final and the constructor is private

    public static int myUtilityMethod(int x) 
        return x * x;
    

你可以调用它

int y = MyUtils.myUtilityMethod(5); // returns 25.

【讨论】:

您能否向我们展示此解决方案的更多代码?关于将enum 用作实用程序类? @JinKwon 我添加了一个示例。您有具体的疑问吗? 不,我只是想知道一个例子。谢谢。我从来没有这样想过。 我通常将 Enums 用于 Utility 类,但 JDK8 的 checkstyle 7.0 不适用于提供的示例... @MarcellodeSales 我已经很多年没有尝试过 checkstyle 了。我更喜欢不仅能发现问题而且还能自动修复问题的工具。 ;)【参考方案4】:

最佳实践是在构造类时抛出错误。

示例:

/**
 * The Class FooUtilityService.
 */
final class FooUtilityService

/**
* Instantiates a new FooUtilityService. Private to prevent instantiation
*/
private FooUtilityService() 

    // Throw an exception if this ever *is* called
    throw new AssertionError("Instantiating utility class.");

【讨论】:

为什么这是最佳实践?我们在这里获得了什么?所有级别都存在愚蠢,但添加这些行只是为了防止实例化(在这些情况下是相当无害的)似乎有点过头了,考虑到六行代码噪音这会引入一个原本干净的类,其中只有常量。 @john16384 就满足现代静态代码分析器而言,这是“最佳实践”,语言创建者认为这是正确的。这对您、您的工作等是否是“最佳实践”取决于您的组织如何看待这种事情。如果你的老板高度重视静态代码分析和压制警告甚至“信息”,那么你的论点应该指向那里。在大型团队环境中,假设处理代码的每个人都知道不实例化任何给定的类可能并不好。这可能属于“防御性编码”的范畴。 @ThomasCarlisle 语言创建者并没有创建那些代码分析器;无用的警告是您可以关闭的(例如在项目中的任何地方不使用序列化时臭名昭著的缺少 serialVersionId 警告)——相反,当有人实例化此类只是为了访问常量时,该工具应该在使用站点发出警告;人们抱怨 Java 很冗长,并且满足由代码分析器中调整不佳的“默认”配置文件强加给我们的任意未经证实的规则也无济于事。【参考方案5】:

添加私有构造函数:

private FilePathHelper()
    super();

【讨论】:

欢迎来到***!不幸的是,这个答案并没有为已经给出的答案添加任何新信息。最好详细说明一下代码如何以及为什么解决问题。【参考方案6】:

将实用程序类设为 final 并添加私有构造函数

【讨论】:

欢迎来到***!不幸的是,这个答案并没有为已经给出的答案添加任何新信息。并且最好详细说明代码如何以及为什么解决问题。 – René Vogt 2016 年 5 月 4 日 5:26【参考方案7】:
public class LmsEmpWfhUtils     
    private LmsEmpWfhUtils() 
     
    // prevents access default paramater-less constructor
    

这可以防止在代码的其他地方使用默认的无参数构造函数。

【讨论】:

【参考方案8】:

SonarQube documentation 建议在类声明中添加 static 关键字。

即把public class FilePathHelper改成public static class FilePathHelper

您也可以添加私有或受保护的构造函数。

public class FilePathHelper

    // private or protected constructor
    // because all public fields and methods are static
    private FilePathHelper() 
    

【讨论】:

那是 C# 文档。【参考方案9】:

我建议在 Sonar 中禁用此规则,引入私有构造函数并没有真正的好处,只是代码库中的冗余字符需要其他人阅读并且计算机需要存储和处理。

【讨论】:

有一个好处。它将防止意外创建该类的冗余实例。 这就是为什么我说“真正的好处”。我知道这条规则背后的“推理”。但这是一个真正的问题吗?这不可能发生 - 应该有人编写代码来创建类,所以这不是偶然的。如果有人创建了一个他不需要的实例,那是他们的问题。简而言之,在我看来,这不值得担心,也不能证明代码库中的额外字符是合理的。 比这更糟糕。 u.method() 看起来像一个实例方法。但是如果uUtilityClass 的一个实例并且(自然)method 是静态的,那么我们就有了一些误导性代码。 private 构造函数的 real 实用程序是它绝对可以防止这种情况发生。 (我看到了吗?是的!)这是谁的问题?程序员使用你的类。并且通过扩展您......因为您将实用程序类设计为可实例化。 Sonar 不仅仅是为了程序员编写代码的好处。这也是为了其他需要使用第一个程序员编写的代码的程序员的利益。 如果您愿意,请启用声纳规则并遵守它。我个人不希望我的代码中有那些额外的字符。不用担心,创建 Utility 类实例的人不会被私有构造函数阻止创建问题。他总是可以引入无限循环、内存泄漏、以难以管理的方式分解代码,或者干脆烧毁办公室。【参考方案10】:

您可以只使用 Lombok 注释来避免不必要的初始化。

使用@NoArgsConstructorAccessLevel.PRIVATE 如下:

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FilePathHelper 
   // your code 

【讨论】:

【参考方案11】:

使用 Lombok 的替代方法是使用 @UtilityClass 注释。

@UtilityClass 在 Lombok v1.16.2 中作为实验性功能引入:

如果一个类用@UtilityClass注解, 它发生了以下事情:

它被标记为final。 如果在其中声明了任何构造函数,则会生成错误。 否则,私有无参数构造函数生成;它会抛出一个UnsupportedOperationException类中的所有方法、内部类和字段都被标记 static

概述:

实用程序类是一个只是函数命名空间的类。它的任何实例都不能存在,并且它的所有成员都是静态的。例如,java.lang.Mathjava.util.Collections 是众所周知的实用程序类。

这个注解自动把被注解的类合二为一。

无法实例化实用程序类。

通过使用@UtilityClass 标记您的类,lombok 将自动生成一个私有构造函数,该构造函数会引发异常,将您添加的任何显式构造函数标记为错误,并将类标记为 final。

如果类是内部类,则该类也标记为static

实用程序类的所有成员都会自动标记为静态。甚至是字段和内部类。

示例:

import lombok.experimental.UtilityClass;

@UtilityClass
public class FilePathHelper 

    private static String resourcesPath;

    public static String getFilePath(HttpServletRequest request) 
        if(resourcesPath == null) 
            ServletContext context = request.getSession().getServletContext();
            String serverpath = context.getRealPath("");               
            resourcesPath = serverpath + "/WEB-INF/classes/";   
        
        return resourcesPath;       
    

来自官方文档的参考:

https://projectlombok.org/features/experimental/UtilityClass

【讨论】:

以上是关于隐藏实用程序类构造函数:实用程序类不应具有公共或默认构造函数的主要内容,如果未能解决你的问题,请参考以下文章

在私有实用程序类构造函数中使用的首选Throwable是什么?

第十一章 实用类

公用的工具类不应该有公共的构造函数

GWT 创建实用程序 javascript 库

为啥具有私有构造函数的类不阻止从此类继承?如何控制哪些类可以从某个基类继承?

超实用的14个 Spring MVC “隐藏”技巧,用了都说好!