为什么Java不提供运算符重载?

时间:2008-09-16 22:04:40

标签: java c++ operator-overloading

从C ++到Java,显而易见的问题是为什么Java不包含运算符重载?

Complex a, b, c; a = b + c;不是Complex a, b, c; a = b.add(c);简单吗?

是否有一个已知原因,的有效参数允许运算符重载?这个理由是随意的还是输给了时间?

17 个答案:

答案 0 :(得分:750)

答案 1 :(得分:41)

James Gosling将Java设计为以下内容:

  当你从一个公寓搬到另一个公寓时,有一个关于移动的原则。一个有趣的实验是打包你的公寓并把所有东西放在盒子里,然后进入下一个公寓而不需要打开任何东西,直到你需要它为止。所以你正在做第一顿饭,而且你正在从盒子里拿出一些东西。然后在一个月左右之后你就用它来弄清楚你生活中你真正需要的东西,然后你拿走了其余的东西 - 忘记你喜欢它多么酷或多酷 - 你只是扔掉它。令人惊讶的是它如何简化你的生活,你可以在各种设计问题中使用这个原则:不做事情只因为它们很酷或只是因为它们很有趣。“

您可以阅读context of the quote here

基本上,运算符重载对于模拟某种点,货币或复数的类非常有用。但在那之后你开始快速耗尽例子。

另一个因素是开发人员重载运算符滥用C ++中的功能,例如'&&','||',强制转换操作符,当然还有'new'。 Exceptional C++一书中详细介绍了将这一点与通过值和异常相结合所带来的复杂性。

答案 2 :(得分:22)

查看Boost.Units:link text

它通过运算符重载提供零开销维度分析。这有多清楚了?

quantity<force>     F = 2.0*newton;
quantity<length>    dx = 2.0*meter;
quantity<energy>    E = F * dx;
std::cout << "Energy = " << E << endl;

实际上会输出“Energy = 4 J”,这是正确的。

答案 3 :(得分:19)

假设您想覆盖a引用的对象的先前值,则必须调用成员函数。

Complex a, b, c;
// ...
a = b.add(c);

在C ++中,此表达式告诉编译器在堆栈上创建三(3)个对象,执行添加,并将复制结果值从临时对象复制到现有对象a

但是,在Java中,operator=不会为引用类型执行值复制,并且用户只能创建新的引用类型,而不能创建值类型。因此,对于名为Complex的用户定义类型,赋值意味着将引用复制到现有值。

改为考虑:

b.set(1, 0); // initialize to real number '1'
a = b; 
b.set(2, 0);
assert( !a.equals(b) ); // this assertion will fail

在C ++中,这会复制该值,因此比较结果将不相等。在Java中,operator=执行引用副本,因此ab现在指的是相同的值。结果,比较将产生“相等”,因为对象将比较等于它自己。

副本和引用之间的区别只会增加操作符重载的混乱。正如@Sebastian所提到的,Java和C#都必须分别处理值和引用相等 - operator+可能会处理值和对象,但operator=已经实现来处理引用。

在C ++中,你应该只是一次处理一种比较,所以它可以减少混乱。例如,在Complex上,operator=operator==都在处理值 - 分别复制值和比较值。

答案 4 :(得分:11)

Java设计者认为运算符重载比它的价值更麻烦。就这么简单。

在一种语言中,每个对象变量实际上都是一个引用,运算符重载至少会给C ++程序员带来额外的非常不合理的危险。将情况与C#的==运算符重载以及Object.EqualsObject.ReferenceEquals(或其所谓的)进行比较。

答案 5 :(得分:8)

Groovy有运算符重载,并在JVM中运行。如果你不介意性能损失(每天变小)。它是基于方法名称自动完成的。例如,'+'调用'plus(argument)'方法。

答案 6 :(得分:6)

我认为这可能是一种有意识的设计选择,迫使开发人员创建名称清楚地传达其意图的功能。在C ++中,开发人员会使运算符重载,这些功能通常与给定运算符的普遍接受性质无关,这使得在不查看运算符定义的情况下几乎无法确定代码片段的作用。

答案 7 :(得分:5)

嗯,你可以在操作员超载的情况下拍摄自己的脚。就像指针一样,人们会对他们犯愚蠢的错误,因此决定将剪刀拿走。

至少我认为这就是原因。 无论如何我都在你身边。 :)

答案 8 :(得分:4)

说操作符重载会导致操作符与操作逻辑不匹配的类型的逻辑错误,就像什么都不说。如果函数名称不适合操作逻辑,则会出现相同类型的错误 - 所以解决方案是什么:放弃函数使用的能力!?  这是一个滑稽的答案 - “不适合操作逻辑”,每个参数名称,每个类,函数或其他任何东西都是逻辑上不合适的。 我认为这个选项应该以可敬的编程语言提供,那些认为它不安全的人 - 嘿,没有他们说你必须使用它。 让我们来看看C#。他们扯了指针,但是嘿 - 有“不安全的代码”声明 - 你喜欢自己编程的程序。

答案 9 :(得分:4)

有人说Java中的运算符重载会导致混淆。让那些人停下来看一些Java代码做一些基本的数学运算,比如用BigDecimal增加一个百分比的财务价值? ......这种运动的冗长使其成为混淆的证明。具有讽刺意味的是,将操作符重载添加到Java将允许我们创建自己的Currency类,这将使这样的数学代码优雅和简单(不那么混淆)。

答案 10 :(得分:4)

从技术上讲,每种编程语言都有运算符重载,可以处理不同类型的数字,例如:整数和实数。说明:术语重载意味着一个函数只有几个实现。在大多数编程语言中,为运算符+提供了不同的实现,一个用于整数,一个用于实数,这称为运算符重载。

现在,很多人发现Java操作符重载为操作符+将字符串添加到一起很奇怪,从数学的角度来看,这确实很奇怪,但从编程语言开发人员的角度来看,添加没有错内置运算符重载为运算符+其他类,例如串。但是,大多数人都同意,一旦为+ for String添加内置重载,那么为开发人员提供此功能通常也是个好主意。

完全不同意运算符重载模糊代码的谬误,因为这是由开发人员决定的。这是天真的想法,而且说实话,它已经老了。

+1用于在Java 8中添加运算符重载。

答案 11 :(得分:2)

假设Java是实现语言,那么a,b和c都将引用类型为Complex的初始值为null。还假设Complex是不可变的,如上面提到的BigInteger和类似的不可变BigDecimal,我认为你的意思是以下,因为你正在为添加b和c返回的Complex指定引用,而不是将此引用与a。

进行比较
  

不是:

Complex a, b, c; a = b + c;
     

更简单。

Complex a, b, c; a = b.add(c);

答案 12 :(得分:1)

有时候运算符重载,友元类和多重继承会很好。

但我仍然认为这是一个好的决定。如果Java有运算符重载,那么在不查看源代码的情况下,我们永远无法确定运算符的含义。目前没有必要。我认为使用方法而不是运算符重载的示例也非常易读。如果你想让事情变得更清楚,你总是可以在毛茸茸的陈述之上添加评论。

// a = b + c
Complex a, b, c; a = b.add(c);

答案 13 :(得分:0)

这不是一个禁止它的理由,而是一个实用的理由:

人们并不总是负责任地使用它。从Python库scapy看这个例子:

>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>

以下是解释:

  

/ operator已被用作两者之间的合成运算符   层。这样做时,下层可以有一个或多个   默认字段根据上层重载。 (你还在   可以给你想要的价值)。字符串可以用作原始图层。

答案 14 :(得分:0)

对Java运算符重载的本机支持的替代方法

由于Java没有运算符重载,因此您可以考虑以下几种选择:

  1. 使用另一种语言。 GroovyScala都具有运算符重载,并且均基于Java。
  2. 使用java-oo,这是一个可以在Java中实现运算符重载的插件。请注意,它不是平台无关的。而且,它存在许多问题,并且与Java的最新版本(即Java 10)不兼容。 (Original StackOverflow Source
  3. 使用JNI,Java本机接口或替代方法。这使您可以编写用于Java的C或C ++(也许是其他?)方法。当然,这也不是平台无关的。

如果有人知道其他人,请发表评论,我会将其添加到此列表中。

答案 15 :(得分:0)

尽管Java语言不直接支持运算符重载,但是您可以在任何Java项目中使用Manifold compiler plugin来启用它。它支持Java 8-13(当前的Java版本),并且在IntelliJ IDEA中得到完全支持。

答案 16 :(得分:0)

我认为做决策的人只是忘记了复杂的值,矩阵代数,集合论和其他情况,而重载将允许使用标准符号而不将所有内容构建到语言中。无论如何,只有基于数学的软件才能真正从这些功能中受益。通用客户应用程序几乎永远不需要它们。

当程序员定义一些特定于程序的运算符作为替代函数时,它们关于不必要的混淆的论点显然是有效的。该函数的名称在清晰可见时会提示它确实存在。运算符是一个没有易读名称的函数。

Java通常是根据这样的哲学设计的:一些额外的详细信息也不错,因为它使代码更具可读性。具有相同功能的构造过去只需键入较少的代码,过去就被称为“语法糖”。例如,这与Python哲学有很大不同,在Python哲学中,即使为第二个读者提供较少的上下文,也总是将较短的时间视为更好的方法。