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

时间:2010-04-29 08:19:37

标签: java constants final

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

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

请分享您的想法。

8 个答案:

答案 0 :(得分:136)

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

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

  

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

答案 1 :(得分: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”语义引入语言。

答案 2 :(得分:7)

C ++中的

const并不意味着值是常量。

C ++中的

const意味着合同的客户承诺不改变其价值。

如果您处于支持基于线程的并发的环境中,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仅仅意味着您的代码在没有强制转换的情况下无法更改它,而不是值是常量。

答案 3 :(得分:5)

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

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

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

这可以追溯到Josh Bloch的 Effective Java 第15项 - 最小化可变性。如果你还没有读过这本书,那就拿起一本副本并阅读几遍,我保证它会带你的比喻“java game”

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

直接引用这本书:

  

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

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

  1. 不提供任何修改对象状态的方法(即setter,又名 mutators
  2. 确保无法扩展类(这意味着将类本身声明为final)。
  3. 制作所有字段final
  4. 制作所有字段private
  5. 确保对任何可变组件的独占访问权限。 (通过制作对象的防御性副本)
  6. 有关详细信息,我强烈建议您阅读该书的副本。

答案 4 :(得分:3)

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

const是一个保留字的事实表明设计师有实施const的想法,但他们已经决定反对它;见this closed bug。所述原因包括添加对C ++样式const的支持会导致兼容性问题。

答案 5 :(得分:-1)

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

答案 6 :(得分:-2)

有两种方法可以定义常量 - conststatic final,具有完全相同的语义。此外,static finalconst

更好地描述了行为

答案 7 :(得分:-3)

我听说有传言说在Java中使用Enums对游戏性能不利。我不知道为什么。 Const会表现得更好......

现实生活中使用Java的CONST示例...只需像这样编码... 然后你的switch语句就可以正常运行了..

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;

switch (this.DataItemEnum) {
    case cRPM:
        percent = (Value - 0.001*Min)/(Max - Min);
        break;
    default:
        percent  = (Value - Min)/(Max - Min);
        break
}