一个类仅包含静态字段和方法是否是不好的实践?

78

我有一个类,它只包含静态成员变量和静态方法。本质上,它充当一个通用工具类。

一个仅包含静态成员变量和静态方法的类是不是不好的实践?


29
java.lang.Math 是一个完全由静态方法组成的类,其构造函数为 private(无法实例化)。 - Asaph
不确定这个问题是否在这个论坛中被问过。但是一个类只有静态字段是一种不好的实践吗?我见过很多这样的类,我认为它们不适合面向对象编程。 - ka3ak
@ka3ak 这个问题的一些答案部分地解决了只有静态字段的类的问题。 - Stephen Watkins
15个回答

91

不,我完全不认为是这样。将一个类充满实例方法但实际上并不依赖于特定实例更加糟糕的做法。将它们作为静态方法告诉用户它们的预期使用方式。此外,这种方式可以避免不必要的实例化。

编辑:事后想一想,总的来说,我认为最好避免仅仅因为某个编程语言特性存在而使用它,或者因为你认为这是“Java的做法”。我记得我的第一份工作中有一个类充满了静态实用方法,其中一位资深程序员告诉我,通过让我所有的方法都是“全局”的,我没有充分利用Java的OO特性。她在团队里待不到6个月。


21
只要类没有内部状态并且本质上是所谓的叶子类(实用程序类属于此类),换句话说,它独立于其他类。这是可以接受的。 Math 类是一个主要例子。

你有关于Leaf/Utility类的好链接或文章吗? - Zoran Pavlovic

14

听起来很合理。

注意:这样做的类通常有一个私有的无参构造函数,这样如果程序员尝试创建静态类的实例,编译器就会产生错误。


5
一个私有默认构造函数还可以防止子类化您的静态类。 - David
1
你有什么理由想要继承一个静态类吗?(我想不出来...我不是语言专家) - Jason S
5
在这种情况下,禁止子类化是一个“优点”。有可能其他人在你的项目中不小心试图对静态类进行子类化(通过拼写错误或误解)。通过添加私有默认构造函数,您可以更严格地强制执行如何使用类,从而使类的含义更加明确。 - David
2
将类声明为final也会防止子类化。 - HexAndBugs

11

对我而言,静态方法不会引起太多担忧(除了测试)。

总体而言,静态成员是一个问题。例如,如果您的应用程序被集群化了怎么办?启动时间呢——正在进行什么样的初始化?要考虑这些问题及更多问题,请查看Gilad Bracha的这篇文章


3
+1 用于区分静态函数(好的)和静态状态(可疑的)。 - mikera

3

这是完全合理的。事实上,在C#中,您可以使用static关键字定义一个类,专门用于此目的。


3

不要过度使用它。请注意,java.lang.Math类仅涉及数学函数。您可能还有一个名为StringUtilities的类,其中包含常见的字符串处理函数,这些函数不在标准API中。但是,如果您的类命名为Utilities,那么这就是您需要将其拆分的提示。


2
请注意,Java特别引入了静态导入:http://en.wikipedia.org/wiki/Static_import 静态导入是Java编程语言中引入的一项功能,它允许在Java代码中使用类中定义为public static的成员(字段和方法),而无需指定该字段所在的类。该功能在5.0版本中引入了该语言。
该功能提供了一种类型安全的机制,可将常量包含到代码中,而无需引用最初定义字段的类。它还有助于废弃创建常量接口的做法:一个仅定义常量的接口,然后编写实现该接口的类,这被认为是接口的不适当使用。
该机制可用于引用类的各个成员。
 import static java.lang.Math.PI;
 import static java.lang.Math.pow;

或者针对一个类的所有静态成员:
 import static java.lang.Math.*;

我认为这与他所问的有点不相关。静态导入与一个类是否仅具有静态成员无关;只要有任何静态成员,它就可以工作。 - danben
1
这是一个既有好信息又有很棒的论点,说明为什么我们应该做这件事情。 - Chuck
抱歉,我不同意 - 我认为这是支持使用静态方法的论点,但它并没有说明不能实例化类。 - danben
@danben:静态导入在你要实例化的类上似乎没有太多意义(至少对我来说是这样)。它最有意义的用法是在“实用类”上,这些类包含许多静态方法,例如java.lang.Math。您不这么认为吗? - Chuck
可能在这里有些吹毛求疵,但当你说它们在你将要实例化的类上没有意义时,我同意你的观点;但是,我不认为它们在不能被实例化的类上更有意义。 - danben
我不确定我是否表达清楚,但我的意思是,在可以实例化的类上使用静态导入静态方法没有任何理由不这样做。 - danben

2

虽然我同意这听起来像是一个合理的解决方案(正如其他人已经提到的),但你可能需要考虑一下,从设计的角度来看,为什么你要为“实用”目的而设立一个类。这些功能是否真正通用于整个系统,还是它们实际上与你架构中某些特定类别的对象有关。

只要你已经考虑过这个问题,我认为你的解决方案没有问题。


1

根据面向对象设计的严格解释,应该避免使用实用类。

问题在于,如果您遵循严格的解释,那么您需要将类强制转换为某种对象才能完成许多事情。

即使是Java设计师也会创建实用类(例如java.lang.Math)

您的选择包括:

double distance = Math.sqrt(x*x + y*y);  //using static utility class

对比:

RootCalculator mySquareRooter = new SquareRootCalculator();
mySquareRooter.setValueToRoot(x*x + y*y);
double distance;
try{
   distance = mySquareRooter.getRoot();
}
catch InvalidParameterException ......yadda yadda yadda.      

即使我们避免了冗长的方法,我们仍然可能会得到:

Mathemetician myMathD00d = new Mathemetician()
double distance = myMathD00d.sqrt(...);

在这个例子中,.sqrt()仍然是静态的,那么创建对象的意义是什么呢?
答案是,在你的其他选择是创建一些没有或很少使用实例变量的人工“Worker”类时,创建实用程序类。

1

Java SDK中的Collections类仅具有静态成员。

因此,只要您有适当的理由--这不是一个糟糕的设计,那么就可以使用它。


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