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


140

我试图确定Java常量背后的原因,我了解到Java允许我们使用final关键字声明常量。

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

请分享您的想法。


2
这里一个const关键词,但没有基本的功能。相应地更正了您的标题和标签。
罗恩侯爵,

Answers:


142

每次我从繁琐的C ++编码过渡到Java时,都要花一些时间来适应Java中缺乏const正确性的问题。const如果您不知道,C ++中的这种用法与仅声明常量变量有很大不同。本质上,它可以确保通过特殊类型的指针(称为const-pointer)进行访问时对象是不可变的。仅包含不应有副作用的方法。不幸的是,这不是由语言强制执行的。

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

有趣的是,Java语言规范将const视为保留关键字(即,不能用作变量标识符的关键字),但未为其分配任何语义。据认为,关键字的保留是为了允许Java语言的扩展以包括C ++样式的const方法和指向const类型的指针而发生的。Java社区流程中用于在Java中实现const正确性的增强请求票证于2005年关闭,这意味着const正确性可能永远不会进入正式的Java规范。


10
但是,Java final与此类似。
reinierpost 2014年

62
不,不是。final例如,该方法的工作方式与C ++ const方法完全不同。
dom0 2014年

7
@reinierpost 属性或变量上final关键字仅确保将属性或变量仅分配一次。例如,仍然可以通过调用某些具有副作用的方法来更改该对象的状态。就引用对象而不是指针而言,C ++的堆栈分配有点类似,但这就是全部。当然,这是dom0已经说过的内容。final
蒂姆(Tim)

9
finalJava const中的值类型似乎像C ++一样工作,但是T&对于引用类型而言,它更像是C ++非常量
Mark K Cowan

1
final是一个精神分裂症的关键字。虽然它可以防止重新分配,但它也用于将变量公开给闭包。我可能想阻止重新分配,但不公开那些变量,但是没有办法。我认为这是一个考虑较深的语言功能。
戴维·布拉德利

82

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

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

为什么或者为什么不const
其次,如果你真的想深入到一些“亲”与“骗子”的论点,看到这个请求增强(RFE)“错误”下的讨论。此RFE请求“只读参考”类型的“常量”功能。“ const”主题于1999年开放,之后于2005年被Sun关闭/拒绝,因此引起了激烈的辩论:

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

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

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

在有人试图就这些原因是好是坏之前就我进行辩论之前,请注意这些不是我的原因。它们只是我从RFE讨论中收集到的某些原因的“要点”。我本人不一定同意他们的意思-我只是想列举为什么有些人(不是我)可能觉得const关键字可能不是一个好主意。我个人希望以明确的方式将更多的“ const”语义引入语言。


2
+1仅是答案的第二部分。许多关键字具有非平凡的语义。就是volatile这么简单的理解吗?还是final?嗯
einpoklum 2015年

OTOH,Java被设计为尽可能少地使用这些非平凡的功能。我并不是说他们实现了这个目标(或者Java并没有偏离那个目标)。但是出于这个原因将其排除可能仍然有其优点。还有其他复杂的事情,就更不引入更多的理由(否则您最终会使用D语言)。
马尔滕·博德威斯

7

const 在C ++中,并不意味着值是一个常数。

const 在C ++中,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 / non-const别名const_cast,因此养成以为const表示不会改变的习惯变得越来越危险。const仅仅意味着您的代码在没有强制转换的情况下无法更改它,而不是值是恒定的。


3
const int x = 42; - x是一个常数

2
@Neil如果您有一个对象或变量被const和非const指针别名,则const别名的值可以被非const别名更改。因此const,并不意味着值是恒定的。这意味着值的客户被约束不能对其进行更改。在您的示例中,没有别名,因此所有用户都受到相同的约束。通常情况并非如此。const影响客户,而不是价值-它表示您无法更改,而不是它不会更改。
皮特·柯卡姆

1
常量正确性与程序员该做什么而不是他们 可以做什么有关。我想大家似乎都明白了这一点。只是想补充一对夫妇美分,可能会感兴趣的因果读者:设计模式,如immutable interfaceimmutable object另一种方式(可打击与演员和反射)在Java中模仿常量。可以使用SealedObject完成“ True” const ,因为它破坏了我们对象的用例。
马丁·安德森

6
应该注意的是程序的结果是不确定的。const_cast不是用来更改const变量的,它是用来将const变量传递给不是const正确的API,但也不要修改该值。我认为认为const不会改变的习惯是个好习惯,因为如果改变了,那就意味着您的程序包含一些hack,这些hack可能会随时中断,具体取决于所使用的编译器。
Cygon

1
修改初生到恒定值是未定义的行为。您的磁盘可能已经格式化。
杨哲

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. 确保以独占方式访问任何可变组件。(通过防御性复制对象)

有关更多详细信息,我强烈建议您拿起这本书的副本。


3
C ++中的const比完全不变性要灵活得多。从某种意义上说,“ const”可以看作是“在此特定上下文中不变”。示例:我有一个不可变的类,但是我想确保不会通过某些公共API对其进行修改。按照Gunslinger47的建议创建接口(并将其返回给该公共API)在Java中实现了相同的目的,但是男孩-它很难看(因此-大多数Java开发人员都忽略了它,从而导致了很多不必要的麻烦)。 。
无错误野兔

3

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

const保留字这个事实表明设计师有实现的想法const,但此后他们决定反对。看到这个封闭的错误。陈述的原因包括添加对C ++样式的支持const会导致兼容性问题。


-1

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


您能提供一些例子吗?
NO_NAME

MYString类实现GetString {private final String aaaa; public String getString(); }类MutableString实现GetString {private String aaaa2; public String getString(); public String setString()}
user1122069'2

-2

定义常量的方式有两种- const和和static final,它们的语义完全相同。此外static final描述行为比const


@Bozho,您说的行为要比Const好,这是什么方式?你可以分享任何例子
gmhk

好吧,变量是static(不属于特定实例)并且final-不能更改。
博佐

-2

您可以使用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样式代码。
hamish

好的,我已删除我的否决票。也许其他一些反对意见是关于switch语句的(它与您的示例其余部分有什么关系?)
afarley

在switch语句中,我将cRPM用作const。考虑到上述情况,完全正确。所以是的,我已经卸下了开关。
hamish
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.