如何在 Java 中定义一个常量类?
Posted
技术标签:
【中文标题】如何在 Java 中定义一个常量类?【英文标题】:How do you define a class of constants in Java? 【发布时间】:2010-10-03 12:49:49 【问题描述】:假设您需要定义一个类,它所做的只是保存常量。
public static final String SOME_CONST = "SOME_VALUE";
这样做的首选方法是什么?
-
接口
抽象类
最后一节课
我应该使用哪一个?为什么?
澄清一些答案:
枚举 - 我不会使用枚举,我不会枚举任何东西,只是收集一些彼此不相关的常量。
接口 - 我不会将任何类设置为实现接口的类。只想用接口调用常量,像这样:ISomeInterface.SOME_CONST
。
【问题讨论】:
这里有一些类似的讨论:***.com/questions/320588/…。我会使用带有私有构造函数的 final 类,这样它就不能被实例化。 抱歉,“我不会使用枚举”将这个问题变成了“做一些愚蠢的事情的最佳方法是什么?” 我不是说你要实现这个接口。但是没有必要使用接口来做到这一点。所以,去最后一堂课吧:) 枚举有什么问题?您总是可以使用它来收集“一些彼此不相关的常量”。嗯? 从概念上讲,如果常量不相关,枚举是一个不好的选择。枚举表示相同类型的替代值。这些常量不是替代品,它们甚至可能不是同一类型(有些可能是字符串、一些整数等) 【参考方案1】:使用最终类。 为简单起见,您可以使用静态导入在另一个类中重用您的值
public final class MyValues
public static final String VALUE1 = "foo";
public static final String VALUE2 = "bar";
在另一个班级:
import static MyValues.*
//...
if(variable.equals(VALUE1))
//...
【讨论】:
在这里创建一个单独的类有什么好处?虽然我通常不会将 Calendar API 作为一个很好的设计示例,但就“日历相关的常量在 Calendar 类中”而言,它很好。 好处是不重复代码,以防您需要在多个类中重用常量。我想你可以很容易地看到这样做的好处。 为什么要复制代码?只需参考其他类。我发现常量真正独立存在的情况相对罕见。 为了更好的可读性,我还有一个没有正文(和匹配的注释)的私有默认构造函数。 相当老的答案,这个评论可能不合适,但我会使用VALUE1.equals(variable)
,因为它可以避免 NPE。【参考方案2】:
您的澄清说明:“我不会使用枚举,我不会枚举任何东西,只是收集一些彼此不相关的常量。”
如果常量之间根本不相关,为什么要将它们收集在一起?将每个常量放在与其最密切相关的类中。
【讨论】:
Jon - 它们是相关的,因为它们都属于相同的功能。但是,它们不是任何事物的枚举......它们不具有对象是“其中之一”的属性。 好的。在这种情况下,只需将它们放在最接近它们相关功能的类中即可。【参考方案3】:我的建议(按偏好降序排列):
1) 不要这样做。在与它们最相关的实际类中创建常量。拥有一个“常量包”类/接口并没有真正遵循 OO 最佳实践。
我和其他所有人都时常忽略 #1。如果你打算这样做,那么:
2) 带有私有构造函数的最终类 这至少可以防止任何人通过扩展/实现“常量包”来轻松访问常量来滥用它。 (我知道你说过你不会这样做——但这并不意味着有人会在你不会这样做)
3) interface 这会起作用,但我不喜欢在 #2 中提及可能的滥用行为。
一般来说,仅仅因为这些是常量并不意味着您不应该对它们应用正常的 oo 原则。如果只有一个类关心一个常量,那么它应该是私有的并且在那个类中。如果只有测试关心一个常量 - 它应该在测试类中,而不是生产代码中。如果在多个地方定义了一个常量(不仅仅是偶然地相同) - 重构以消除重复。等等 - 像对待方法一样对待它们。
【讨论】:
1的问题是有时你会在你的代码中注入一些对另一个层类的依赖来获取常量。 接口中的所有字段都是隐式公共的、静态的和最终的 @Kodebetter 但是可以扩展/实现接口以添加更多字段。【参考方案4】:正如 Joshua Bloch 在 Effective Java 中所说:
接口只能用于定义类型, 抽象类不会阻止实例化(它们可以被子类化,甚至建议它们被设计为子类化)。如果所有常量都相关(如行星名称),您可以使用 Enum,将常量值放在与它们相关的类中(如果您可以访问它们),或者使用不可实例化的实用程序类(定义私有默认构造函数)。
class SomeConstants
// Prevents instanciation of myself and my subclasses
private SomeConstants()
public final static String TOTO = "toto";
public final static Integer TEN = 10;
//...
然后,如前所述,您可以使用静态导入来使用您的常量。
【讨论】:
【参考方案5】:我的首选方法是根本不这样做。当 Java 5 引入类型安全枚举时,常量的时代几乎消失了。甚至在此之前,Josh Bloch 发布了一个(略显冗长)版本,该版本适用于 Java 1.4(及更早版本)。
除非您需要与某些遗留代码互操作,否则真的没有理由再使用命名字符串/整数常量。
【讨论】:
好答案,举个例子会更好。【参考方案6】:只使用 final 类。
如果您希望能够添加其他值,请使用抽象类。
使用接口没有多大意义,接口应该指定一个合同。您只想声明一些常量值。
【讨论】:
【参考方案7】:enums 不是这类东西的最佳选择吗?
【讨论】:
99% 的时间。但并非总是如此。【参考方案8】:enum
s 很好。 IIRC,有效 Java(第 2 版)中的一项具有 enum
常量枚举标准选项,为任何值实现 [Java 关键字] interface
。
我的偏好是使用 [Java 关键字] interface
而不是 final class
作为常量。你隐含地得到了public static final
。有些人会争辩说,interface
允许糟糕的程序员实现它,但糟糕的程序员会写出无论你做什么都很糟糕的代码。
哪个更好看?
public final class SomeStuff
private SomeStuff()
throw new Error();
public static final String SOME_CONST = "Some value or another, I don't know.";
或者:
public interface SomeStuff
String SOME_CONST = "Some value or another, I don't know.";
【讨论】:
但是不管你做什么,糟糕的程序员都会写出很烂的代码。这是真的。如果您可以采取措施减轻不良编程或完全消除不良编程,那么您就有责任这样做;尽可能明确。糟糕的程序员无法扩展最终类。 @liltitus27 在某种程度上我同意。但是你真的想要丑陋的代码来阻止糟糕的程序员编写糟糕的代码吗?他们生成的代码仍然毫无希望,但现在你的代码可读性降低了。【参考方案9】:或者 4. 将它们放在包含使用常量的逻辑最多的类中
...抱歉,无法抗拒;-)
【讨论】:
这不是一个好主意。这会在应该没有的类之间创建依赖关系。 为什么不呢?我会说使用相同常量的类无论如何都有依赖关系......而且一定有一个类最依赖这些常量。 如果只有一个类使用常量,这可能有意义。【参考方案10】:对我来说最好的方法是enum
:
public enum SomeApiConstants ;
public static final String SOME_CONST = "SOME_VALUE";
//may be in hierarchy
public enum ApiMapping ;
public static final String VERSION = "/version";
public static final String VERSION_LIST = "/list/type";
优点:
干净的代码 不需要定义私有构造函数 尝试实例化在编译时被验证为java: enum types may not be instantiated
防止克隆和反序列化
【讨论】:
【参考方案11】:私有构造函数的一个缺点是方法的存在永远无法测试。
枚举的自然概念很好地应用于特定的域类型,将其应用于分散的常量看起来不够好
枚举的概念是“枚举是密切相关项的集合”。
扩展/实现常量接口是一种不好的做法,很难考虑要求扩展不可变常量而不是直接引用它。
如果应用像SonarSource这样的质量工具,有规则迫使开发人员放弃常量接口,这是一件尴尬的事情,因为很多项目都喜欢常量接口,很少看到“扩展”的事情发生在常量接口上
【讨论】:
“私有构造函数是永远无法测试的方法的存在”:不正确。如果您(或您的老板)真的对代码覆盖率报告为 100% 感到偏执,您仍然可以通过使用反射并确保构造函数抛出异常来测试这一点。但是,恕我直言,这只是形式,并不需要进行测试。如果您(或团队)只真正关心真正重要的事情,则不需要 100% 的线路覆盖率。以上是关于如何在 Java 中定义一个常量类?的主要内容,如果未能解决你的问题,请参考以下文章