我试图找出Java中常量的原因
我了解到Java允许我们使用final关键字声明常量。
我的问题是为什么Java没有引入Constant(const)功能。 由于很多人说它来自C ++,在C ++中我们有const关键字。
请分享你的想法。
-
有一个const关键字,但没有底层功能。 相应地更正了标题和标签。
每次我从繁重的C ++编码转向Java,我都需要一点时间来适应Java中缺乏const-correctness的问题。如果您不知道,在C ++中使用const与仅声明常量变量有很大不同。本质上,它确保一个对象在通过一种称为const-pointer的特殊指针访问时是不可变的当在Java中,在我通常想要返回一个const指针的地方时,我改为返回一个带有接口类型的引用仅包含不应有副作用的方法。不幸的是,langauge没有强制执行。
维基百科提供以下有关该主题的信息:
Interestingly, the Java language specification regards const as a reserved keyword — i.e., one that cannot be used as variable identifier — but assigns no semantics to it. It is thought that the reservation of the keyword occurred to allow for an extension of the Java language to include C++-style const methods and pointer to const type. The enhancement request ticket in the Java Community Process for implementing const correctness in Java was closed in 2005, implying that const correctness will probably never find its way into the official Java specification.
-
但是,Java的final类似。
-
不,不是。 final方法的示例工作与C ++ const方法完全不同。
-
@reinierpost属性或变量上的final关键字只确保属性或变量仅分配给一次。人们仍然可以通过调用某些带副作用的方法来改变这个对象的状态。 final有点类似于在引用对象而不是指针方面堆叠C ++的分配,但就是这样。除了dom0已经说过的话之外,这也是当然的。
-
Java中的final对于值类型似乎像C ++ const一样工作,但对于引用类型更像是C ++非const T&
-
final是一个精神分裂症的关键词。虽然它阻止了重新分配,但它也用于将变量暴露给闭包。我可能想要阻止重新分配但不暴露这些变量,但是没有办法做到这一点。在我看来,这是一个相当糟糕的语言特征。
-
final创建运行时常量,而const将创建编译时常量。换句话说,如果我们声明一个字段final,它可以在构造函数内部更改,如果声明static final,它可以在static initializer block内更改,但如果你想要在声明后不允许更改该字段,然后需要const,目前无法使用。
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只是答案的第二部分。许多关键字具有非平凡的语义。 volatile这么简单易懂吗?还是final?咩。
C ++中的const并不意味着值是常量。
C ++中的const意味着合同的客户承诺不改变其价值。
如果您处于支持基于线程的并发的环境中,const表达式的值是否更改将变得更加明显。
由于Java从一开始就被设计为支持线程和锁并发,所以它不会因为重载术语而产生混淆,以获得final具有的语义。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #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,但作为非const别名创建,x不是常量。并非每个编译器都需要volatile来执行此行为(尽管允许每个编译器内联常量)
对于更复杂的系统,您可以在不使用const_cast的情况下获得const /非const别名,因此养成认为const意味着某些内容不会改变的习惯变得越来越危险。 const仅仅意味着您的代码在没有强制转换的情况下无法更改它,而不是值是常量。
-
const int x = 42; - x是常数
-
@Neil我认为他在谈论对象
-
@andy为对象工作相同。
-
@Neil我的意思是:当Pete写道const并不意味着值是不变的时,我的理解是他只谈论原始类型。进一步解释(我很欣赏你知道这些东西):对于const非对象变量(比如说int),编译器会强制执行契约。如果我将其声明为const则无法更改。对于一个对象,我必须声明为const的任何修改对象内部状态的成员函数。如果我没有那么它可能有一个(内部)可变的类的const实例。这是我对他所写内容的理解。
-
@Neil如果const和非const指针都有一个别名或变量别名,那么const别名的值可以通过非const别名来改变。因此,const并不意味着值是恒定的。这意味着值的客户端被约束不会改变它。在您的示例中没有别名,因此所有用户都处于相同的约束下。一般情况并非如此。 const影响客户,而不是价值 - 它说你不能改变它,而不是它不会改变。
-
我在考虑这些物体
-
正确性是程序员应该做的,而不是他们能做什么。我想你们似乎都有这一点。只是想添加一些可能对因果读者感兴趣的美分:像immutable interface和immutable object这样的设计模式是另一种方式(可以用强制转换和反射来模仿)来模仿Java中的const。"True"const可以用SealedObject完成,唉它会破坏我们对象的用例。
-
应该注意到你的程序的结果是不确定的。 const_cast不用于更改const变量,它是将const变量传递给不是const正确的API,但也不修改值。我认为考虑const不会改变的习惯是一个好习惯,因为如果它们确实改变了,那就意味着你的程序包含的hack可能会随时根据所使用的编译器而中断。
-
示例中的@Cygon显示您可以使用hack来创建非const别名。但并非所有非常量别名都是黑客,例如将c和c+1传递给strncpy将违反strncpy中源不会改变的任何假设。
-
@PeteKirkham确切地说,因为将重叠的内存传递给strncpy()也是一个黑客攻击 - 它使你处于未定义的区域:"如果字符串重叠,则strncpy的行为是不确定的"(GNU C Library,任何类似的警告)其他C库)。只有memmove()是一个例外,在库实现中需要特殊的调整以防止别名。
-
修改生成到常量值是未定义的行为。您的磁盘可能已经格式化。
-
@ZheYang这是一个例子。如果你有一个线程改变一个对象作为const引用传递给另一个对象,并且该线程假定它不会改变,那么更现实的问题往往会出现,但这意味着更长的演示代码。
-
@anon声称const对于对象和基本整数类型的工作方式相同是完全错误的。那不是真的。例如,请参阅此SO问题中的讨论。
-
声称它outputs 42 then 7是错误的;行为未定义。关键是x被声明为const(因此,例如,它可以放在只读存储器中)。在这个例子中,编译器完全忽略赋值并打印42两次是有效的。如果x声明没有const并且作为const引用传递给另一个函数,则const_cast将是合法的。
这是一个古老的问题,但我认为无论如何我会贡献我的2美分,因为这个话题在今天的谈话中出现了。
这并不完全回答为什么没有const?但是如何使你的类不可变。 (遗憾的是,我还没有足够的声誉作为对已接受答案的评论发布)
保证对象不变性的方法是更仔细地设计你的类是不可变的。这需要比可变类更多的关注。
这可以追溯到Josh Bloch的有效Java项目15 - 最小化可变性。如果你还没有读过这本书,那就拿起一本副本并阅读几遍,我保证它会提升你的比喻"java游戏"。
在项目15中,Bloch建议您应该限制类的可变性以确保对象的状态。
直接引用这本书:
An immutable class is simply a class whose instances cannot be modified. All of the information contained in each instance is provided when it is created and is fixed for the lifetime of the object. The Java platform libraries contain many immutable classes, including String, the boxed primitive classes, and BigInte- ger and BigDecimal. There are many good reasons for this: Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.
然后Bloch通过遵循5个简单的规则来描述如何使您的类不可变:
不提供任何修改对象状态的方法(即setter,aka mutators)
确保无法扩展类(这意味着将类本身声明为final)。
使所有字段final。
使所有字段private。
确保对任何可变组件的独占访问。 (通过制作对象的防御性副本)
有关详细信息,我强烈建议您阅读本书的副本。
-
C ++中的const比全面的不变性更灵活。从某种意义上说,"const"可以被视为"在这个特定的语境中不可变"。示例:我有一个不可变的类,但我想确保它不会通过某些公共API进行修改。根据Gunslinger47的建议制作一个接口(并为该公共API返回它)在Java中实现了同样的目的,但是男孩 - 它很丑陋(因此 - 它被大多数Java开发人员忽略,导致显着的不必要的混乱)。 。
const的C ++语义与Java final非常不同。如果设计师使用过const,则会产生不必要的混乱。
const是一个保留字的事实表明设计师有实现const的想法,但他们已经决定反对它;看到这个封闭的bug。所述原因包括添加对C ++样式const的支持会导致兼容性问题。
有一种方法可以在Java中创建"const"变量,但仅适用于特定的类。只需定义一个具有最终属性的类并将其子类化。然后使用你想要使用"const"的基类。同样,如果需要使用"const"方法,请将它们添加到基类中。编译器不允许您修改它认为是基类的最终方法的内容,但它将读取并调用子类上的方法。
-
你能提供一些这方面的例子吗?
-
class MYString实现GetString {private final String aaaa; public String getString(); } MutableString实现GetString {private String aaaa2; public String getString(); public String setString()}
您可以使用static final创建类似于Const的东西,我过去曾使用过它。
1 2 3 4 5 6 7 8
| 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样式代码。
-
好的,我删除了我的downvote。也许其他一些downvotes是关于switch语句的(它与你的例子的其余部分有什么关系?)
-
在switch语句中,我使用了cRPM,就像它是一个const一样。考虑到上述情况,这是对的。所以是的,我已经删除了开关。
有两种方法可以定义常量 - const和static final,具有完全相同的语义。此外,static final描述了比const更好的行为
-
@Bozho,你说比Const更好的行为,它是什么方式?你能分享任何一个例子吗?
-
好吧,变量是static(不属于特定实例),final - 不能改变。