您如何在Java中定义常量类?


89

假设您需要定义一个仅包含常量的类。

public static final String SOME_CONST = "SOME_VALUE";

这样做的首选方式是什么?

  1. 接口
  2. 抽象类
  3. 最终班

我应该使用哪一个?为什么?


澄清一些答案:

枚举-我将不使用枚举,我不会枚举任何东西,只是收集一些彼此不相关的常量。

接口-我不会将任何类设置为实现该接口的类。只是想使用该接口来调用常量,如下所示:ISomeInterface.SOME_CONST


这里有一些类似的讨论:stackoverflow.com/questions/320588/…。我将使用带有私有构造函数的最终类,以使其无法实例化。
丹·代尔

2
抱歉,“我不打算使用枚举”将这个问题变成“做某事愚蠢的最佳方法是什么?”
cletus

我并不是说您要实现该接口。但是没有必要使用接口来做到这一点。因此,请参加最后一堂课
:)

枚举有什么问题?您可以始终使用它来收集“一些彼此不相关的常量”。嗯?
gedevan

5
从概念上讲,如果常量不相关,则枚举是一个不好的选择。枚举表示相同类型的替代值。这些常量不是替代品,它们甚至可能不是同一类型(某些常量可能是字符串,某些整数等)
Dan Dyer

Answers:


92

使用期末课程。为简单起见,您可以使用静态导入在另一个类中重用您的值

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

在另一堂课中:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}

4
在这里创建一个单独的类的好处是什么?尽管我通常不把Calendar API作为设计的一个很好的例子,但就“与日历相关的常量在Calendar类中”而言,这很好。
乔恩·斯基特

7
好处是不重复代码,以防万一您需要在多个类中重用常量。我想您可以轻松看出这一点的优势。
user54579

2
为什么要重复代码?只是参考其他课程。我发现一个常数真正独立存在是相对罕见的。
乔恩·斯基特

14
为了更好的可读性,我还要有一个没有主体的私有默认构造函数(以及匹配的注释)。
冉·比隆

5
答案很老了,此评论可能不合时宜,但我会VALUE1.equals(variable)避免使用NPE。
Aakash

38

您的澄清指出:“我不会使用枚举,我不会枚举任何东西,只是收集一些彼此不相关的常量。”

如果这些常量根本不相关,那么为什么要一起收集它们呢?将每个常量都放在与其最相关的类中。


2
乔恩-它们在某种意义上是相互关联的,它们都属于同一功能。但是,它们不是任何事物的枚举……它们不具有对象是“其中之一”的属性。
Yuval Adam

13
好的。在这种情况下,只需将它们放在最接近与其相关功能的类中。
乔恩·斯基特

26

我的建议(按优先级从高到低的顺序):

1)不要这样做。在最相关的实际类中创建常量。拥有“常量包”类/接口并不是真正遵循OO最佳实践。

我和其他所有人不时忽略#1。如果您要这样做,那么:

2)使用私有构造函数的最终类这至少可以防止任何人通过扩展/实现它来轻松访问常量来滥用您的“常量包”。(我知道你说过你不会这样做-但这并不意味着有人会在你不愿意之后出现)

3)界面这将起作用,但我不愿意在#2中提及可能的滥用。

通常,仅仅因为这些是常量并不意味着您仍然不应该对它们应用常规的oo原理。如果只有一个类关心某个常量,则该常量应该是私有的,并且在该类中。如果仅测试关心常量,则应在测试类中,而不是生产代码中。如果在多个位置定义了一个常量(不只是偶然),请重构以消除重复。依此类推-像对待方法一样对待它们。


2
1的问题是,有时您会在代码中注入一些对另一层类的依赖,以获取常量。
amdev '18

接口中的所有字段都隐式为public,static和final
Kodebetter

@Kodebetter但是可以扩展/实现接口以添加更多字段。
机智

12

正如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;
    //...
}

然后,如前所述,您可以使用静态导入来使用常量。


7

我的首选方法是完全不执行此操作。当Java 5引入类型安全枚举时,常数时代几乎消失了。甚至在此之前,Josh Bloch都发布了该版本(稍微有些罗word),该版本适用于Java 1.4(或更早版本)。

除非您需要与某些旧代码的互操作性,否则实际上不再需要使用命名的String / integer常量。


2
好的答案,一个例子会更好。
amdev

3

只需使用最后一堂课。

如果您希望能够添加其他值,请使用抽象类。

使用接口没有太大意义,接口应该指定合同。您只想声明一些常量值。



3

enums很好。IIRC,有效的Java(第二版)中的enum一项具有常量,该常量枚举interface为任何值实现[Java关键字]的标准选项。

我的选择是使用[关键字的Java]interface在一个final class常量。您隐式获得public static final。有人会争辩说,aninterface允许不良的程序员来实现它,但是不良的程序员将编写代码,无论您做什么都糟透了。

哪个看起来更好?

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.";
}

但是糟糕的程序员将编写无论​​您做什么都很烂的代码。那是真的。如果您可以采取措施减轻不良编程或完全消除不良编程,那么您就有责任这样做。尽可能明确。糟糕的程序员无法扩展最终课程。
liltitus13年

1
@ liltitus27在某种程度上,我同意。但是,您是否真的希望丑化的代码只是为了阻止不良程序员编写不良代码的一种方式?他们产生的代码仍然是绝望的,但是现在您的代码可读性降低了。
汤姆·哈特芬

1

或者4.将它们放在包含最常使用常量的逻辑的类中

...对不起,无法抗拒;-)


3
这不是一个好主意。这将在类之间创建依赖关系,而该类之间应该没有依赖关系。
Yuval Adam

为什么不?我要说的是,使用相同常量的类总有一个依赖项……而且某种程度上,肯定有一个类最依赖于这些常量。
Simon Groenewolt,2009年

如果只有一类使用常量,这可能是有道理的。
汤姆·霍顿

-1
  1. 私有构造函数的缺点之一是方法的存在是无法测试的。

  2. 通过本质概念枚举可以很好地应用于特定的域类型,将其应用于分散的常量看起来不够好

枚举的概念是“枚举是一组紧密相关的项目”。

  1. 扩展/实现常量接口是一种不好的做法,很难考虑需要扩展不可变常量而不是直接引用它。

  2. 如果应用诸如SonarSource之类的质量工具,则有规则迫使开发人员放弃常量接口,这很尴尬,因为许多项目都喜欢常量接口,而很少看到“扩展”在常量接口上发生的事情


1
“私有构造函数是永远无法测试的方法的存在”:不正确。如果您(或您的老板)对代码覆盖率报告确实是100%感到非常偏执,您仍然可以通过使用反射并确保构造函数引发异常来对此进行测试。但是,恕我直言,这只是形式而已,实际上并不需要进行测试。如果您(或团队)只关心真正重要的事情,则100%的线路覆盖率是不必要的。
L. Holanda
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.