我该如何创建一个工具类?

79

我想创建一个包含实用方法的类,例如

public class Util {

   public static void f (int i) {...}

   public static int g (int i, int j) {...}

}

创建一个工具类的最佳方法是什么?

我应该使用私有构造函数吗?

我应该将工具类设置为抽象类吗?

我应该什么都不做吗?


4
编程时通常没有最佳的做事方式。 - William Reed
创建Utility类的最干净的方法 https://projectlombok.org/features/experimental/UtilityClass - adimoh
1
我应该什么都不做吗?多么禅意。 - awwsmm
@WilliamReed 实际上,“最好”是具体情境相关的。 :D - Sreekanth Karumanaghat
@SreekanthKarumanaghat 是的,我同意。自从我留下那条评论以来,我的观点已经发生了很大变化。 - William Reed
使用lombok中的@UtilityClass注解来实现实用类。 - user811602
4个回答

145

对于一个完全无状态的Java实用类,我建议将该类声明为publicfinal,并具有私有构造函数以防止实例化。 final关键字可以防止子类化并可以提高运行时效率。

该类应包含所有static方法,不应声明为abstract(因为这会意味着该类不是具体的并且必须以某种方式进行实现)。

该类应该被赋予一个与其提供的实用程序集相对应的名称(或者如果该类要提供各种未分类的实用程序,则为“Util”)。

该类不应包含嵌套类,除非嵌套类也是实用类(虽然这种做法可能很复杂,影响可读性)。

类中的方法应具有适当的名称。

仅由类本身使用的方法应为私有。

该类不应具有任何非最终/非静态类字段。

该类还可以被其他类静态导入以提高代码的可读性(但这取决于项目的复杂性)。

示例:

public final class ExampleUtilities {
    // Example Utility method
    public static int foo(int i, int j) {
        int val;

        //Do stuff

        return val;
    }

    // Example Utility method overloaded
    public static float foo(float i, float j) {
        float val;

        //Do stuff

        return val;
    }

    // Example Utility method calling private method
    public static long bar(int p) {
        return hid(p) * hid(p);
    }

    // Example private method
    private static long hid(int i) {
        return i * 2 + 1;
    }
}

或许最重要的是,每种方法的文档应该精确而且描述准确。这个类中的方法很可能会被经常使用,因此拥有高质量的文档来补充代码是很好的。


5
我强调一下,高质量的文档应该包括完整的JavaDoc注释,而不是代码注释。 - Brian Kelly
2
@initramfs 这是一个很好的回答,但您能否根据您所写的内容调整示例以使其一致? - karlihnos
或许最重要的是,每个方法的文档应该是精确和描述性的。这个类中的方法很可能会经常被使用,因此拥有高质量的文档来补充代码是非常好的。请参考 https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html 更新上述代码的javadoc注释。 - razvanone

23
根据 Joshua Bloch (《Effective Java》) 的建议,应使用私有构造函数并始终抛出异常。这将最终阻止用户创建实用程序类的实例。
不建议将类标记为抽象,因为“抽象”暗示着该类是为继承而设计的。

1
我不明白。如果构造函数是私有的且类不会被实例化,为什么需要在私有构造函数中抛出异常呢? - Johnny Five
1
@JohnnyFive 的私有构造函数抛出异常是 Joshua Block 的一个概念,旨在推广开发人员阅读代码并告诉他们不应该创建任何实例。 - michaldo
2
@JohnnyFive 引发异常,甚至可以抛出 AssertionError,因为使用反射仍然可以访问私有构造函数。通过抛出异常或错误,您可以保证不会创建类的任何实例。 - Felix S
1
@JohnnyFive 从书中得知,“AssertionError并非严格必要,但它提供了保险,以防构造函数意外地从类内部调用。” - Leponzo

9
我会把这个类设置为final,并且所有方法都是static的。这样这个类就不能被扩展了,而这些方法可以通过Classname.methodName来调用。如果你添加成员,请确保它们是线程安全的;)

5
创建一个 abstract 类会向代码读者传递一个信息,即您希望使用该 abstract 类的用户进行子类化。然而,如果您不希望用户这样做,则应该避免这种情况:实用程序类不应该被子类化。
因此,在此处添加一个私有构造函数是更好的选择。您还应将类设为 final,以禁止对实用程序类进行子类化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接