关于java:使用常量的病态

Morbid use of constants

我为什么要写(正如我的同事所说):

1
2
import static org.apache.commons.lang.math.NumberUtils.INTEGER_ONE;
if (myIntVariable == INTEGER_ONE) { ... }

代替:

1
if (myIntVariable == 1) { ... }

我知道建议使用常量,但我认为NumberUtils.INTEGER_ONE的值永远不会改变! 所以我写1


你不应该。 INTEGER_ONE名称没有比1更有意义。但是,如果此值具有其他含义(例如,一年中的月份),则使用常量(如Calendar.FEBRUARY)将使您的代码更清晰。

我可以猜测Commons Math库中的这个常量是在没有Integer缓存和自动装箱的情况下在Java 1.4中创建的,所以你可以在不同的地方重用相同的Integer对象(不是原始int)。以节省内存。所以它是出于性能原因而添加的,而不是代码清晰度。现在它已经过时了:即使你需要一个Integer对象,你也可以使用Integer.valueOf(1)或隐式自动装箱并获得缓存的自动装箱。


你不应该写INTEGER_ONE!你也不应该写1(见下面的例外)!

为什么?像1这样的文字被称为幻数。幻数是"具有无法解释的意义或多次出现的唯一值,可以(最好)用命名常量替换"(来自同一维基百科页面的解释)。

所以通常应该做的是将这些神奇的数字变成常量,其名称代表或解释该数字的含义。常数INTEGER_ONE没有解释其含义。

所以你真正需要做的是在这个上下文中找到值的含义,并创建一个具有该名称的常量。例如,如果1表示允许的最大线程数,则应该有一个常量:

1
static final int MAX_NUMBER_OF_THREADS = 1;

根据Tagir的评论编辑

如果文字本身在您编写代码的域中具有含义,则不应将其替换为命名常量。 Tagir用于计算逆元素的示例是一个很好的例子:

1
2
3
double invert(double x) {
    return 1/x;
}

这里的文字1在数学域内的这个上下文中有意义。所以它可以按原样使用。


我碰巧为我的公司写了风格指南,我建议如下:

Don't use hard coded,"magic" values. If a value is constant, define it as such.
Numbers such as -1, 0, 1, 2, 100 can be used in some situations.

我的例子在Objective-C中,因为这是我编写指南的语言,但规则仍然适用。

好用法

1
2
3
4
5
6
7
8
9
10
static NSString* const DatabaseName = @"database.name";

//Acceptable use of"2"
float x = (ScreenWidth / 2) - (ImageWidth / 2);

//Acceptable use of 0
for (int i = 0; i < NumberOfItems; i++)

//Acceptable use of 100, but only because the variable is called"percentage"
float percentage = (someObjects * 100) / allObjects.count;

用法不好

1
2
3
4
5
6
float x = (480 / 2) - (120 / 2); //We have to guess these are sizes?

//Unneccessary constants.
for (int i = ZERO; i < NumberOfItems; i += ONE)

float percentage = (someObjects.count * 100) / 120; //What is 120?

org.apache.commons.lang.math.NumberUtils.INTEGER_ONE它??为您提供final static Integer对象而不是原始int 1,因为它是final static,它充当常量并可用于比较Integer对象,因为它将始终返回相同的实例。

因此,在上面的场景中它可能看起来不合适,但如果你在比较时使用它,肯定会有影响。

而且,尽可能多地使用常量而不是硬编码:

  • 它可以使您的代码易于维护。如果将来发生任何变更情况,您只能在一个地方进行更改。
  • 代码看起来更干净,更易读。

  • 从Class NumberUtils你会看到它被定义为:

    1
    2
    /** Reusable Integer constant for one. */
    public static final Integer INTEGER_ONE = new Integer(1)

    所以,你会看到INTEGER_ONE与1不同。它是一个已经为你构建的对象。因此,如果我们需要Integer(1)的实例,而不是创建自己的实例,则可以重用库中的一个来节省时间和内存。

    这实际上取决于你的应用程序,如果你确实是int版本的1,那么,你可能更好地使用它而不是这个Integer类。


    您可能知道它是否永远不会改变,但如果我开始编辑您的代码我就不会...

    基本上它是一种在实际代码中记录代码的方法。使用这样的常量和示例的原因是为了避免代码中的Magic Numbers及其缺点。

    这就是说,你可以使用它到一个不再有利的地方,并且杂乱诱导。我倾向于为不止一次使用过的东西做这件事,或者有被我或其他人改变的概念......或者用更简单的术语来表达重要的价值观。


    想象一下,你有这个

    1
    if (myIntVariable == 1) { ... }

    但几千次......

    突然之间需要成为2。

    你更容易改变什么?

    编辑:在downvoting之前,我从不使用幻数的优点的角度回答,我不以任何方式(我认为这是可推断的,来到人们)建议改变库常数。