一个更基本的原因Java不包括运算符重载(至少对于赋值)?

Dan*_*aum 3 c++ java operator-overloading

关于Java中没有运算符重载这一事实的2年左右的讨论(为什么Java不提供运算符重载?),并且从很多C++年代开始到Java,我想知道是否还有更多运算符重载不是Java语言的一部分的根本原因,至少在赋值的情况下,而不是答案底部附近的最高级别答案(即James Gosling的个人选择).

具体来说,考虑分配.

// C++
#include <iostream>

class MyClass
{
public:
    int x;
    MyClass(const int _x) : x(_x) {}
    MyClass & operator=(const MyClass & rhs) {x=rhs.x; return *this;}
};

int main()
{
    MyClass myObj1(1), myObj2(2);
    MyClass & myRef = myObj1;
    myRef = myObj2;

    std::cout << "myObj1.x = " << myObj1.x << std::endl;
    std::cout << "myObj2.x = " << myObj2.x << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

myObj1.x = 2
myObj2.x = 2
Run Code Online (Sandbox Code Playgroud)

但是,在Java中,该行myRef = myObj2(假设myRef前一行中的声明是myClass myRef = myObj1,如Java所要求的那样,因为所有这些变量都是自动Java风格的"引用")表现得非常不同 - 它不会导致myObj1.x更改,输出将是

myObj1.x = 1
myObj2.x = 2
Run Code Online (Sandbox Code Playgroud)

C++和Java之间的这种差异使我认为Java中缺少运算符重载,至少在赋值的情况下,不是James Gosling的"个人选择问题",而是Java的基本必要性.将所有对象变量视为引用的语法(即MyClass myRef = myObj1定义myRef为Java样式引用).我这样说是因为如果Java中的赋值导致左侧引用引用不同的对象,而不是允许对象本身改变其值的可能性,那么似乎不可能提供重载的赋值运算符.

换句话说 - 它不仅仅是一种"选择",甚至没有"屏住呼吸"的可能性,希望它会被引入,因为前面提到的高评价答案也说明了(接近结束).引用:" 现在不添加它们的原因可能是内部政治,对功能的过敏,开发人员的不信任(你知道,破坏者),与以前的JVM的兼容性,编写正确规范的时间等等.所以不要屏住呼吸等待这个功能. " < - 所以这是不正确的,至少对于赋值运算符:没有运算符重载(至少对于赋值)的原因是Java本质的基础.

这是我的正确评估吗?

附录

假设赋值运算符是一个特殊情况,那么我的后续问题是:是否有任何其他运算符,或者更普遍的任何其他语言功能,必然会以与赋值运算符类似的方式受到影响?我想知道Java和C++之间关于变量作为值/引用的差异有多深.也就是说,在C++中,变量标记表示值(并注意,即使变量标记最初被声明为引用,它仍然被视为基本上在任何地方使用的值),而在Java中,变量标记表示诚实到良好的引用以后使用令牌的地方.

Dav*_*eas 5

在讨论Java和C++之间的相似点和不同点时,存在一个很大的误解,这在你的问题中出现了.C++引用和Java引用不一样.在Java中,引用是真实对象的可重置代理,而在C++中,引用是对象的别名.用C++术语来说,Java引用是垃圾收集指针而不是引用.现在,回到您的示例,使用C++和Java编写等效代码,您将不得不使用指针:

int main() {
   type a(1), b(2);
   type *pa = &a, *pb = &b;
   pa = pb;
   // a is still 1, b is still 2, pa == pb == &b
}
Run Code Online (Sandbox Code Playgroud)

现在示例是相同的:赋值运算符应用于指向对象的指针,在这种特殊情况下,您也不能在C++中重载运算符.重要的是要注意操作符重载很容易被滥用,这是首先避免它的一个很好的理由.现在,如果添加两种不同类型的实体:对象和引用,那么事情就会变得更加混乱.

如果允许您operator=在Java中为特定对象重载,那么您将无法对同一对象进行多次引用,并且语言将被削弱:

Type a = new Type(1);
Type b = new Type(2);
a = b;                 // dispatched to Type.operator=( Type )??
a.foo();
a = new Type(3);       // do you want to copy Type(3) into a, or work with a new object?
Run Code Online (Sandbox Code Playgroud)

这反过来会使语言中的不可用的类型:集装箱储存引用,他们重新分配(即使在创建对象时正好第一次),功能不真正使用过,通过引用语义,而是pass-通过值引用(这是一个完全不同的问题,再次,不同的是void foo( type* )void foo( type& ):代理实体被复制,你不能修改引用调用者传递英寸

问题是该语言正在努力隐藏这样一个事实,aa 引用的对象不是同一个事物(在C#中也是如此),这反过来意味着您无法明确声明要应用一个操作引用/引用,由语言解决.该设计的结果是任何可以应用于引用的操作都不能应用于对象本身.

从其他运算符开始,决策很可能是任意的,因为语言隐藏了引用/对象差异,它可以设计为由编译器a+b转换成type* operator+( type*, type* ).因为你不能使用算术,所以没有问题,因为编译器会认识到这a+b是一个必须应用于对象的操作(它对引用没有意义).但随后它可以被认为是一个有点尴尬,你可以重载+,但你不能超载=,==,!=...

这是C#采用的路径,其中赋值不能为引用类型重载.有趣的是,在C#中有值类型,可以为引用和值类型重载的运算符集是不同的.没有在大型项目中编写C#,我无法确定这种混淆的潜在来源是否是这样或者人们是否已经习惯了(但是如果你搜索SO,你会发现很少有人会问为什么X不能超载C#用于引用类型,其中X是可以应用于引用本身的操作之一.