从c++到Java,一个显而易见的未回答的问题是为什么Java没有包含操作符重载?
复合物不是a b c吗?A = b + c;比复合物a, b, c简单得多;A = b.add(c);?
是否有一个已知的原因,不允许操作符重载的有效参数?理由是随意的,还是被时间消磨了?
从c++到Java,一个显而易见的未回答的问题是为什么Java没有包含操作符重载?
复合物不是a b c吗?A = b + c;比复合物a, b, c简单得多;A = b.add(c);?
是否有一个已知的原因,不允许操作符重载的有效参数?理由是随意的,还是被时间消磨了?
当前回答
假设您想要覆盖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=执行引用复制,因此a和b现在引用相同的值。结果,比较将产生'equal',因为对象的比较结果将等于自身。
复制和引用之间的差异只会增加操作符重载的混乱。正如@Sebastian所提到的,Java和c#都必须分别处理值和引用相等——operator+可能会处理值和对象,但operator=已经被实现来处理引用。
在c++中,一次只能处理一种比较,这样就不会那么令人困惑。例如,在Complex上,operator=和operator==都处理值——分别复制值和比较值。
其他回答
Java操作符重载本机支持的替代方案
因为Java没有操作符重载,这里有一些你可以考虑的替代方案:
使用另一种语言。Groovy、Scala和Kotlin都有操作符重载,它们都是基于Java的。 使用Java -oo,这是一个在Java中支持操作符重载的插件。注意,它不是平台独立的。此外,它有许多问题,并且与Java的最新版本(即Java 10)不兼容。(StackOverflow原始源码) 使用JNI、Java本机接口或替代方案。这允许您编写在Java中使用的C或c++方法。当然,这也不是平台独立的。
如果有人知道其他人,请评论,我会把它添加到这个列表中。
假设Java是实现语言,那么a、b和c都是对初始值为null的Complex类型的引用。还假设Complex是不可变的,就像前面提到的BigInteger和类似的不可变BigDecimal一样,我想你是指下面的意思,因为你将引用赋值给从添加b和c返回的Complex,而不是将这个引用与a进行比较。
不是: 复合物a, b, c;A = b + c; 简单得多: 复合物a, b, c;A = b.add(c);
我认为人们在做决定时忘记了复杂的值、矩阵代数、集合论和其他情况,因为重载可以使用标准的符号,而不用把所有东西都构建到语言中。无论如何,只有面向数学的软件才能真正受益于这些特性。一般的客户应用程序几乎不需要它们。
当程序员定义一些特定于程序的操作符时,这些关于不必要混淆的参数显然是有效的,而这些操作符可能是函数。函数名在清晰可见时,提供了函数名存在的提示。运算符是一个没有可读名称的函数。
Java通常是按照这样的理念设计的:一些额外的冗长并不是坏事,因为它使代码更具可读性。具有相同功能的构造只需输入更少的代码,过去被称为“语法糖”。这与Python的哲学非常不同,例如,在Python哲学中,更短的几乎总是被视为更好的,即使为第二个读者提供的上下文更少。
这不是一个很好的理由来禁止它,而是一个实际的理由:
人们并不总是负责任地使用它。请看下面这个来自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 |>>
下面是解释:
/操作符用作两个操作符之间的复合操作符 层。当这样做时,下层可以有一个或多个它的 默认字段根据上层重载。(你仍然 可以给出你想要的值)。字符串可以用作原始层。
James Gosling把设计Java比作:
"There's this principle about moving, when you move from one apartment to another apartment. An interesting experiment is to pack up your apartment and put everything in boxes, then move into the next apartment and not unpack anything until you need it. So you're making your first meal, and you're pulling something out of a box. Then after a month or so you've used that to pretty much figure out what things in your life you actually need, and then you take the rest of the stuff -- forget how much you like it or how cool it is -- and you just throw it away. It's amazing how that simplifies your life, and you can use that principle in all kinds of design issues: not do things just because they're cool or just because they're interesting."
你可以在这里阅读引用的上下文
基本上,运算符重载对于对点、货币或复数建模的类非常有用。但在那之后你很快就会用光例子。
另一个因素是开发人员在c++中滥用了这个特性,重载了'&&'、'||'、强制转换操作符,当然还有'new'。将此与传递值和异常相结合所产生的复杂性在《例外c++》一书中有详细介绍。