【问题标题】:Why is there no Constant feature in Java?为什么Java中没有Constant特性?
【发布时间】:2011-02-13 17:04:21
【问题描述】:

我试图找出 Java 中常量背后的原因 我了解到Java允许我们使用final关键字来声明常量。

我的问题是为什么 Java 没有引入常量 (const) 功能。由于很多人说它来自C++,所以在C++中我们有const关键字。

请分享你的想法。

【问题讨论】:

  • 一个const关键字,但没有底层特征。相应地更正了您的标题和标签。

标签: java constants final


【解决方案1】:

您可以使用 static final 创建类似于 Const 的东西,我过去曾使用过。

protected static final int cOTHER = 0;
protected static final int cRPM = 1;
protected static final int cSPEED = 2;
protected static final int cTPS = 3;
protected int DataItemEnum = 0;

public static final int INVALID_PIN = -1;
public static final int LED_PIN = 0;

【讨论】:

  • 反对基于谣言的优化,因为我听说这是一种无效的策略。
  • 我从答案中删除了谣言。您仍然可以使用 static final int 创建 const 样式代码。
  • 好的,我已经删除了我的反对票。也许其他一些反对意见与 switch 语句有关(它与您的示例的其余部分有什么关系?)
  • 在 switch 语句中,我使用 cRPM,就好像它是一个常量一样。非常正确,考虑到上述情况。所以是的,我已经移除了开关。
【解决方案2】:

这是一个有点老的问题,但我想无论如何我都会贡献我的 2 美分,因为今天这个话题出现在对话中。

这并不能完全回答 为什么没有 const? 而是 如何 让你的类不可变。 (不幸的是,我还没有足够的声誉来发布对已接受答案的评论)

保证对象不变性的方法是更仔细地设计你的类以使其不可变。这比可变类需要更多的关注。

这可以追溯到 Josh Bloch 的 Effective Java 第 15 条 - 最小化可变性。如果你还没有读过这本书,请拿起一本读几遍,我保证它会成为你的比喻“java游戏”

在第 15 条中,布洛赫建议您应该限制类的可变性以确保对象的状态。

直接引用该书:

不可变类只是一个实例不能被修改的类。每个实例中包含的所有信息都是在创建时提供的,并且在对象的生命周期内是固定的。 Java 平台库包含许多不可变的类,包括 String、装箱的原始类以及 BigInteger 和 BigDecimal。这有很多很好的理由:不可变类比可变类更容易设计、实现和使用。它们不易出错且更安全。

然后,Bloch 描述了如何通过以下 5 条简单规则使您的类不可变:

  1. 不要提供任何修改对象状态的方法(即,setter,又名 mutators
  2. 确保类不能被扩展(这意味着将类本身声明为final)。
  3. 将所有字段设为final
  4. 将所有字段设为private
  5. 确保对任何可变组件的独占访问。 (通过制作对象的防御性副本)

如需了解更多详情,我强烈建议您阅读本书。

【讨论】:

  • C++ 中的 const 比全面的不变性灵活得多。从某种意义上说,“const”可以被视为“在这个特定的上下文中是不可变的”。示例:我有一个不是不可变的类,但我想确保它不会通过某些公共 API 进行修改。按照 Gunslinger47 的建议制作一个接口(并为该公共 API 返回它)在 Java 中实现了同样的事情,但是男孩 - 它很丑陋(因此 - 它被大多数 Java 开发人员忽略,导致严重的不必要的混乱).. .
【解决方案3】:

有一种方法可以在 Java 中创建“const”变量,但仅限于特定的类。只需定义一个具有最终属性的类并将其子类化。然后在你想使用“const”的地方使用基类。同样,如果您需要使用“const”方法,请将它们添加到基类中。编译器不允许你修改它认为是基类的最终方法,但它会读取和调用子类上的方法。

【讨论】:

  • 你能举个例子吗?
  • class MYString 实现 GetString { private final String aaaa;公共字符串 getString(); } 类 MutableString 实现 GetString { 私有字符串 aaaa2;公共字符串 getString();公共字符串 setString() }
【解决方案4】:

const 的 C++ 语义与 Java final 非常不同。如果设计师使用了const,那将会造成不必要的混乱。

const 是保留字这一事实表明设计者有实现const 的想法,但他们后来决定不这样做;见this closed bug。所述原因包括添加对 C++ 样式 const 的支持会导致兼容性问题。

【讨论】:

    【解决方案5】:

    const 是什么意思
    首先,意识到“const”关键字的语义对不同的人意味着不同的东西:

    • 只读引用 - Java final 语义 - 引用变量本身不能重新分配以指向另一个实例(内存位置),但实例本身是可修改的
    • 只读引用 - C const 指针/引用语义 - 表示此引用不能用于修改实例(例如,不能分配给实例变量,不能调用可变方法) - 仅影响引用变量,因此指向同一个实例的非常量引用可以修改该实例
    • 不可变对象 - 表示实例本身不能修改 - 适用于实例,因此任何非 const 引用都不允许或不能用于修改实例
    • 以上的一些组合
    • 其他人

    为什么或为什么不const
    其次,如果您真的想深入研究一些“赞成”与“反对”的论点,请参阅此增强请求(RFE)“错误”下的讨论。此 RFE 请求“只读参考”类型“const”功能。 1999 年开放,然后在 2005 年被 Sun 关闭/拒绝,“const”话题引起了激烈的争论:

    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4211070

    虽然双方都有很多很好的论据,但反对const 的一些经常被引用(但不一定令人信服或明确)的理由包括:

    • 可能有混淆的语义,可能被误用和/或滥用(请参阅上面的const 是什么意思
    • 可能会复制其他可用的功能(例如,设计不可变类、使用不可变接口)
    • 可能是功能蠕变,导致需要进行其他语义更改,例如支持按值传递对象

    在有人试图争论我这些是好是坏的理由之前,请注意这些不是我的理由。它们只是我从浏览 RFE 讨论中收集到的一些原因的“要点”。我自己不一定同意他们的观点——我只是想说明为什么有些人(不是我)可能觉得const 关键字可能不是一个好主意。就个人而言,我希望以明确的方式将更多“const”语义引入语言。

    【讨论】:

    • +1 仅用于您答案的第二部分。许多关键字具有非平凡的语义。 volatile这么简单好理解吗?还是final?嗯。
    • OTOH,Java 旨在尽可能少地使用这些重要功能。我并不是说他们实现了那个目标(或者 Java 并没有偏离那个目标)。但出于这个原因排除它可能仍然有好处。还有其他复杂的事情是不介绍更多的原因(否则您最终会使用 D 语言)。
    【解决方案6】:

    C++ 中的const 并不意味着值是常量。

    const 在 C++ 中意味着合约的客户承诺不改变其价值。

    如果您处于支持基于线程的并发的环境中,const 表达式的值是否变化会变得更加明显。

    由于 Java 从一开始就被设计为支持线程和锁并发,因此它并没有因为重载术语以具有 final 所具有的语义而增加混乱。

    例如:

    #include <iostream>
    
    int main ()
    {
        volatile const int x = 42;
    
        std::cout << x << std::endl;
    
        *const_cast<int*>(&x) = 7;
    
        std::cout << x << std::endl;
    
        return 0;
    }
    

    输出 42 然后 7。

    虽然x 标记为const,但由于创建了非常量别名,x 不是常量。并非每个编译器都需要 volatile 来实现此行为(尽管每个编译器都允许内联常量)

    对于更复杂的系统,您会在不使用const_cast 的情况下获得 const/non-const 别名,因此养成认为 const 意味着某些东西不会改变的习惯变得越来越危险。 const 仅表示您的代码在没有强制转换的情况下无法更改它,而不是该值是恒定的。

    【讨论】:

    • const int x = 42; - x 是一个常数
    • @Neil 如果您有一个对象或变量同时被 const 和非常量指针别名,那么 const 别名的值可以通过非常量别名来更改。因此const 并不意味着值是恒定的。这意味着一个值的客户端被约束不能改变它。在您的示例中,没有别名,因此所有用户都受到相同的约束。一般情况并非如此。 const 影响客户,而不是价值 - 它说你不能改变它,而不是它不会改变。
    • Const-correctness 是关于程序员应该做什么,而不是他们可以做什么。我想你们似乎都明白了这一点。只是想为因果读者增加几分钱:immutable interfaceimmutable object 之类的设计模式是在 Java 中模仿 const 的另一种方式(可通过强制转换和反射击败)。 "True" const 可以用SealedObject 完成,可惜它破坏了我们对象的用例。
    • 应该注意你的程序的结果是未定义的。 const_cast 不是用来更改 const 变量的,它是用来将 const 变量传递给不正确的 API,但也不修改值。我认为认为 const 不会改变的习惯是一个好习惯,因为如果它们确实改变了,这意味着您的程序包含可能随时中断的 hack,具体取决于所使用的编译器。
    • 声称它outputs 42 then 7是错误的;行为未定义。关键是x声明 const(因此,例如,它可以放在只读内存中)。在此示例中,编译器完全忽略分配并打印 42 两次是有效的。如果 x 被声明为 没有 const 并作为 const 引用传递给另一个函数,则 const_cast 是合法的。
    【解决方案7】:

    每次我从繁重的 C++ 编码转到 Java 时,我都需要一点时间来适应 Java 中 const-correctness 的不足。如果您不知道,C++ 中const 的这种用法与仅声明常量变量有很大不同。本质上,它确保当通过一种称为 const 指针的特殊指针访问时对象是不可变的。在 Java 中,在我通常想要返回 const 指针的地方,我改为返回具有接口类型的引用仅包含不应有副作用的方法。不幸的是,这不是由语言强制执行的。

    维基百科提供了有关该主题的以下信息:

    有趣的是,Java 语言规范将 const 视为保留关键字——即不能用作变量标识符的关键字——但没有为其分配任何语义。人们认为保留关键字是为了允许对 Java 语言进行扩展,以包括 C++ 风格的 const 方法和指向 const 类型的指针。 Java Community Process 中用于在 Java 中实现 const 正确性的增强请求票已于 2005 年关闭,这意味着 const 正确性可能永远不会进入官方 Java 规范。

    【讨论】:

    • Java 的 final 类似。
    • 不,不是。例如,final 方法的工作方式与 C++ const 方法完全不同。
    • @reinierpost final 关键字在属性或变量上 只是确保属性或变量只分配给一次。仍然可以通过例如调用具有副作用的某些方法来更改此对象的状态。 final 在引用对象而不是指针方面有点类似于 C++ 的堆栈分配,但仅此而已。这当然是 dom0 已经说过的。
    • final 在 Java 中似乎像 C++ const 用于值类型,但更像是 C++ 非常量 T&amp; 用于引用类型
    • final 是一个精神分裂症关键字。虽然它可以防止重新分配,但它也用于将变量公开给闭包。我可能想防止重新分配但不公开这些变量,但没有办法做到这一点。在我看来,这是一个经过深思熟虑的语言功能。
    【解决方案8】:

    定义常量的方法有两种——conststatic final,语义完全相同。此外,static finalconst 更好地描述了这种行为

    【讨论】:

    • @Bozho,你说的行为比 Const 更好,它是什么方式?你能分享任何例子吗
    • 嗯,变量是static(不属于特定实例)和final - 无法更改。
    猜你喜欢
    • 1970-01-01
    • 2010-10-17
    • 2011-06-04
    • 2019-12-17
    • 2012-01-10
    • 2012-02-02
    • 1970-01-01
    相关资源
    最近更新 更多