【问题标题】:Morbid use of constants常数的病态使用
【发布时间】:2015-10-06 21:59:32
【问题描述】:

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

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

代替:

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

?

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

【问题讨论】:

  • 如果它是您自己的自定义常量,那么以这种方式使用是有意义的。
  • 也许你可以问你的同事这个​​问题?他/她可能有一个我们不知道的特定理由使用它。
  • @FlorentBayle 我的同事在任何地方都使用常量!他用常数替换了每个数字或字符串!他对我的 Why 的回答是“文档说要使用常量!”;是的,他太吹毛求疵了。
  • @Michele:你问过哪个文档说要使用常量吗?
  • 官方代码约定中提到了如何处理常量。当语言设计者有关于如何处理常量的官方指南时,无需以“基于意见”的方式关闭。

标签: java constants


【解决方案1】:

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

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

【讨论】:

  • 但是您不应该将== 用于整数对象。
  • 对于它的价值,如果使用Calendar.FEBRUARY1 更具表现力,那么INTEGER_ONE 也是出于同样的原因,它明确表示它只是一个整数,并且不是某种序数。 虽然,我想不出值得区分的情况。
  • @Captain Man:应该没有理由明确表示一个数字就是一个数字,因为在好的代码中,所有文字数字都应该是一个数字,因为所有其他数字都由命名表示常量。
  • 具有讽刺意味的是,旧代码中的这些常量会导致相反的结果。由于这样的旧代码使用new Integer(1),它是唯一的地方,其中装箱1 使用unshared 对象。
  • @Jean-FrançoisSavard,这是因为这个库保留了没有 valueOf 方法的 Java 1.4 兼容性。
【解决方案2】:

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

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

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

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

static final int MAX_NUMBER_OF_THREADS = 1;

根据 Tagir 的评论进行编辑

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

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

这里的文字1 在数学领域内的上下文中具有含义。所以可以直接使用。

【讨论】:

  • 这一切都很好,但是您还必须自己推理将其保存在自己的变量中的理由。您不应该对所有数字都这样做,至少在现实意义上是这样。
  • @insidesin 哦,这是错误的。您应该对几乎所有有意义的文字执行此操作。但这是一个可能接近基于意见的讨论。
  • 这样的方法怎么样:double invert(double x) {return 1/x;}?我也应该在这里避免1 吗?那么如何命名常量呢?
  • 实际上,在我知道的许多编码标准中,0 和 1 有充分的理由免除 No-Magic_Number 规则,因为通常不代表将来会改变的数字,但是例如某物的存在与否(类似于 bool)
  • @Tagir Valeev:1/x1 常量的正确名称是 IDENTITY_ELEMENT_WITH_RESPECT_TO_THE_DIVISION_BINARY_OPERATION。不要惊讶我也更喜欢在那里使用文字1...
【解决方案3】:

我恰好为我的公司编写了风格指南,我建议如下:

不要使用硬编码的“魔法”值。如果一个值是常量,那么定义它。 在某些情况下可以使用 -1、0、1、2、100 等数字。

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

良好的用法

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;

使用不当

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?

【讨论】:

    【解决方案4】:

    org.apache.commons.lang.math.NumberUtils.INTEGER_ONE 它给你一个final static Integer 对象而不是原始的int 1,因为它是final static,它充当一个常量,可以用于比较Integer 对象,因为它总是返回相同的实例。

    因此,在上述情况下,它可能看起来不适合,但如果您在比较时使用它,它肯定会产生影响。

    此外,我们应该尽可能倾向于使用常量而不是硬编码,因为:

    1. 它可以使您的代码易于维护。如果将来发生任何需要更改的情况,您只能在一个地方进行更改。
    2. 代码看起来更简洁、更易读。

    【讨论】:

    • 我同意你回答的第一部分,但你给出的使用常量的两个一般原因在这里并不适用。
    • @MikeMB 我提供它们不是为了支持我的陈述,而是作为一种正常的编码约定。
    • 那我建议你稍微改一下那部分。在你说:总是喜欢常量,在一个你不应该喜欢那个常量的问题的上下文中。
    • @MikeMB 已编辑,感谢您指出。向你们学习总是好的:)
    • 关于您的第一段:Java 在检查容器时是否认为具有相同值但来自不同来源的键是不同的?如果是这样,那听起来很可怕。
    【解决方案5】:

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

    基本上,这是一种在实际代码中记录代码的方式。像这样使用常量和例子的原因是为了避免Magic Numbers in code and their disadvantages.

    话虽如此,您可以将其使用到不再具有优势并导致混乱的程度。我倾向于为不止一次使用的东西或有被我或其他人改变的概念......或更简单的术语重要值。

    【讨论】:

    • 我不同意幻数论点。 INTEGER_ONE 永远是 1。
    • @CKing INTEGER_ONE 是什么意思?是在世界崩溃之前您能猜出多少次您的登录详细信息,还是更简单一些?
    • 这正是重点:INTEGER_ONE 没有比文字 1 更多的意义,因此就代码的清晰度和可维护性而言,它没有任何优势。
    • 那么整个问题就无关紧要了,他的问题将是关于糟糕的变量命名约定而不是幻数。我只是在给他怀疑的好处,并假设他可怜的变量名是“口误”。
    【解决方案6】:

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

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

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

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

    【讨论】:

    • Integer.valueOf(int) 不做同样的事情吗?
    【解决方案7】:

    想象一下你有这个

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

    但是几千次...

    突然 in 需要成为 2。

    你更容易改变什么?

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

    【讨论】:

    • 那么您希望 OP 更改 Commons Math 库并将 INTEGER_ONE 设置为 2 吗?
    • 所以你建议改变 INTEGER_ONE == 2?
    • 我认为这个问题更倾向于使用常量,而不是apache特定的INTEGER_ONE
    • @Luis:相反,第二部分表明这个问题是明确的关于INTEGER_ONE
    • @insidesin:他没有命名它来自库的常量。而且这个常数的名字并不差。它的目的是充当某种类型的数字(不同于字面量 1)。它的目的绝不是成为某个参数的描述性名称(这是大多数人在说“首选常量”时所想到的)。因此,所有不直接在代码中使用 1 的参数,因为它是一个幻数也直接适用于 INTEGER_ONE,而且它是更多样板,因此可读性较差。 Tagir Valeev 解释了为什么从性能的角度来看也不再需要它。
    猜你喜欢
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 2021-06-24
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多