为什么Java中没有常量功能?

gmh*_*mhk 134 java final constants

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

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

请分享你的想法.

Gun*_*r47 137

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

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

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

  • 不,不是.例如,`final`方法与C++`const`方法完全不同. (58认同)
  • 但是Java的`final`是类似的. (10认同)
  • Java中的`final`似乎像值C++`const`一样用于值类型,但更像是C++非const`T&`用于引用类型 (9认同)
  • @reinierpost属性或变量*上的`final`关键字*只是确保属性或变量仅被分配给******.人们仍然可以通过调用一些带副作用的方法来改变这个对象的状态.`final`在某种程度上类似于在引用对象而不是指针方面堆叠C++的分配,但就是这样.除了dom0已经说过的话之外,这也是当然的. (7认同)

Ber*_*t F 81

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

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

为什么或为什么不呢const
第二,如果你真的想深入研究一些"专业"与"反对"的论点,请参阅此增强请求(RFE)"bug"下的讨论.该RFE请求"可读参考"类型的"const"特征.在1999年开放,然后在2005年被Sun关闭/拒绝,"const"主题得到了激烈的争论:

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

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

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

在任何人试图讨论这些是好是坏的原因之前,请注意这些不是我的理由.它们只是我从略读RFE讨论中收集到的一些原因的"要点".我自己并不一定同意这些 - 我只是想引用为什么有些人(不是我)可能觉得某个const关键字可能不是一个好主意.就个人而言,我更喜欢以明确的方式将更多"const"语义引入语言.

  • +1仅适用于答案的第二部分.许多关键字具有非平凡的语义."易变"是如此简单易懂吗?还是'最终'?咩. (2认同)

Pet*_*ham 7

const 在C++中并不意味着值是常量.

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;
}
Run Code Online (Sandbox Code Playgroud)

输出42然后7.

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

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

  • 应该注意到你的程序的结果是不确定的.const_cast不用于更改const变量,它是将const变量传递给不是const正确的API,但也不修改值.我认为考虑const不会改变的习惯是一个好习惯,因为如果它们确实改变了,那就意味着你的程序包含的hack可能会随时根据所使用的编译器而中断. (5认同)
  • const int x = 42; - x是常数 (3认同)
  • @Neil 如果您有一个对象或变量同时被 const 和非常量指针作为别名,那么 const 别名的值可以由非常量别名更改。因此`const` 并不意味着一个值是常数。这意味着值的客户端被限制不能改变它。在您的示例中没有别名,因此所有用户都受到相同的约束。一般情况下并非如此。`const` 影响客户,而不是价值——它表示你不能改变它,而不是它不会改变。 (2认同)
  • 声称它“输出 42 然后 7”是错误的;行为未定义。关键是 `x` 是 **declared** `const`(因此,例如,它可以放在只读内存中)。在这个例子中,编译器完全忽略赋值并打印 42 两次是有效的。如果 `x` 已被声明为 **没有** `const` 并作为常量引用传递给另一个函数,则 const_cast 将是合法的。 (2认同)

gre*_*ego 5

这是一个古老的问题,但我认为无论如何我会贡献我的2美分,因为这个话题在今天的谈话中出现了.

这并不完全回答为什么没有const?但是如何使你的类不可变.(遗憾的是,我还没有足够的声誉作为对已接受答案的评论发布)

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

这可以追溯到Josh Bloch的有效Java 项目15 - 最小化可变性.如果你还没有读过这本书,那就拿起一本副本并阅读几遍,我保证它会提升你的比喻"java游戏".

在项目15中,Bloch建议您应该限制类的可变性以确保对象的状态.

直接引用这本书:

不可变类只是一个无法修改其实例的类.每个实例中包含的所有信息都在创建时提供,并在对象的生命周期内得到修复.Java平台库包含许多不可变类,包括String,盒装基元类以及BigInteger和BigDecimal.这有很多很好的理由:不可变类比可变类更容易设计,实现和使用.它们不容易出错并且更安全.

然后Bloch通过遵循5个简单的规则来描述如何使您的类不可变:

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

有关详细信息,我强烈建议您阅读本书的副本.

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