为什么java中不允许赋值给’this’?

我从编译器得到的错误是“赋值的左侧必须是变量”。 我的用例是深度复制,但并不真正相关。

在C ++中,可以分配给*this

问题不在于如何规避this分配。 这很简单,但更确切地说,决定不将其变为变量的理由是什么。

原因是技术性的还是概念性的?

我的猜测到目前为止 – 以随机方法重建Object的可能性容易出错(概念性),但技术上可行。

编辑请限制“因为java规范如此说”的变化。 我想知道决定原因

在C ++中,可以分配给*this

是的,但你无法做到this = something C ++中的this = something ,我实际上认为它与你在Java方面所要求的内容更接近。

[…]决定不this变为变量的原因背后有什么理由。

我会说清晰度/可读性。

this被选为保留字,可能因为它没有作为方法的显式参数传递。 将它用作普通参数并能够为其重新分配新值会严重损害可读性。

事实上,由于这个原因,许多人认为你根本不应该改变论证变量。

原因是技术性的还是概念性的?

我认为主要是概念性的。 但是会出现一些技术怪癖。 如果您可以this重新分配值,则可以完全隐藏局部变量后面的实例变量。

我的猜测到目前为止 – 以随机方法重建Object的可能性容易出错(概念性),但技术上可行。

我不确定我是否完全理解这一陈述,但是,容易出错可能是决定将其作为关键词而不是变量的主要原因。

原因是技术性的还是概念性的?

IMO,概念。

this关键字是“对当前正在执行其方法的对象的引用”的简写。 你不能改变那个对象是什么。 它在Java执行模型中毫无意义。

由于改变它没有任何意义, this将其变为变量是没有意义的。

(请注意,在C ++中,您要分配给*this ,而不是this 。而在Java中,没有*运算符,也没有真正的等价物。)


如果您认为可以在中途飞行中更改方法的目标对象,那么这里有一些反问题。

  • 这样做有什么用? 这个(假设的)语言特征会帮助您解决哪些问题……这些问题无法以更容易理解的方式解决?

  • 你会如何处理互斥量? 例如,如果在同步方法的中间分配给this ,会发生什么……并且所提出的语义是否有意义? (问题是你最终在一个你没有锁定的对象上以同步方法执行…或者你必须解锁旧的this并将新的锁定与其所带来的复杂性。此外,这对于互斥体的设计目标有何意义?)

  • 你怎么会理解这样的事情:

     class Animal { foo(Animal other) { this = other; // At this point we could be executing the overridden // Animal version of the foo method ... on a Llama. } } class Llama { foo(Animal other) { } } 

    当然你可以将语义归于此,但是:

    • 你已经以一种难以理解的方式打破了子类的封装,并且
    • 你实际上没有取得任何特别有用的东西。

如果你认真地回答这些问题,我希望你能得出结论,实现这个问题本来是个坏主意。 (但如果你有满意的答案,我建议你把它们写下来,然后把它们贴在你自己的问题答案上!)

但实际上,我怀疑Java设计者甚至不仅仅考虑了这个想法。 (这是正确的,IMO)


*this = ...forms的C ++实际上只是当前对象属性的一系列赋值的简写。 我们已经可以在Java中用一系列正常的赋值来做到这一点。 当然不需要新的语法来支持这一点。 (一个类多久一次从另一个类的状态重新初始化?)

我注意到你评论如此:

我想知道this = xy;的语义是什么this = xy; 应该。 你认为它应该怎么做? – JimmyB 11年11月2日12:18

如果xy属于正确类型,则将其引用设置为xy使“原始”对象符合gc条件 – kostja 2011年11月2日12:24

那不行。

  1. 在调用方法时, this的值(有效地)通过值传递给方法。 被调用者不知道this引用来自何处。

  2. 即使它确实如此,那只是举行参考的地方。 除非在所有位置都分配了null否则该对象不能进行垃圾回收。

忽略这在技术上是不可能的事实,我不认为你的想法会有用或有助于编写可读/可维护的代码。 考虑一下:

 public class MyClass { public void kill(MyClass other) { this = other; } } MyClass mine = new MyClass(); .... mine.kill(new MyClass()); // 'mine' is now null! 

你为什么想这么做? 假设方法名称是无害的而不是kill ,你会期望该方法能够消除mine的价值吗?

我不。 事实上,我认为这将是一种错误:无用且危险。

即使没有这些令人讨厌的“使其无法访问”的语义,我实际上也没有看到任何改善this情况的好用例。

this甚至不是变数。 它是Java语言规范中定义的关键字:

当用作主表达式时,关键字this表示一个值,该值是对调用实例方法的对象(第15.12节)的引用,或者是对正在构造的对象的引用

因此,这是不可能的,因为无法为while分配值。

因为thisfinal

this是关键字,而不是变量。 并且您无法为关键字指定内容。 现在考虑一下它是否是设计规范中的参考变量,并参见下面的示例

它包含对象调用方法的隐式引用。 它仅用于参考目的,现在考虑你为此分配一些东西, this它不会破坏一切吗?

String类中考虑以下代码(注意:下面的代码包含编译错误,它只是为了演示OP的情况)

  public CharSequence subSequence(int beginIndex, int endIndex) { //if you assign something here this = "XYZ" ; // you can imagine the zoombie situation here return this.substring(beginIndex, endIndex); } 

Java中的这一部分是语言的一部分,是一个关键词,而不是一个简单的变量。 它用于从其中一个方法访问对象,而不是另一个对象。 为它分配另一个对象会导致混乱。 如果要在对象中保存另一个对象引用,只需创建一个新变量。

原因只是概念性的。 this是为了访问Object本身,例如在方法中返回它。 就像我说的那样,如果你要为它分配另一个引用,它会造成混乱。 告诉我为什么改变this是有道理的。

在C ++中分配给(*this)执行复制操作 – 将对象视为值类型

Java没有对类使用值类型的概念。 对象分配始终是引用

复制对象就好像它是一个值类型: 如何在Java中复制对象?


用于Java的术语令人困惑: Java是“传递引用”还是“传递值”

答案: Java按值传递引用。 (从这里 )

换句话说,因为Java从不将非基元视为值类型,所以每个类型变量都是一个引用(实际上是一个指针)。

因此,当我说“对象分配总是按引用”时,将术语称为“对象分配总是由引用的值 ”可能在技术上更准确。

Java所绘制的区别的实际含义总是被传递给值,这体现在“ 如何在java中创建我的交换函数? ”这一问题及其答案: 您不能 。 诸如C和C ++之类的语言能够提供交换function,因为它们与Java不同,允许您通过使用对该变量的引用从任何变量进行分配 – 从而允许您更改其值(如果是非常量)而不更改它之前引用的对象的内容。

它可以让你的头脑旋转,试图一直思考这一点,但这里什么都没有……

  • Java类类型变量总是“引用”,它们实际上是指针。
  • Java指针是原始类型。
  • Java赋值总是由底层基元的值(在这种情况下为指针)。
  • Java只是没有相当于C / C ++传递引用的机制,它允许你间接修改一个独立的基本类型,它可能是一个像this的“指针”。

另外,值得注意的是,C ++实际上有两种不同的语法用于传递引用。 一个基于显式指针,并且inheritance自C语言。 另一个基于C ++ 引用类型运算符& 。 [还有C ++ 智能指针forms的参考管理,但这更类似于类似Java的语义 – 其中引用本身是按值传递的。]

注意:在上面的讨论中, assign-bypass-by通常是可互换的术语。 任何赋值的基础是一个概念运算符函数 ,它根据传入的右侧对象执行赋值


所以回到最初的问题 :如果你可以在Java中分配this ,那就意味着要改变这个引用的值。 这实际上相当于在C ++中直接赋予this ,这在该语言中也是不合法的。

在Java和C ++中, this实际上是一个无法修改的指针。 Java 似乎有所不同,因为它使用了. 运算符取消引用指针 – 如果您习惯使用C ++语法,则会给您一个不是一个的印象。

当然,您可以用Java编写类似于C ++ 拷贝构造函数的东西 ,但与C ++不同,没有办法解决实现需要根据显式成员初始化提供的事实。 。 [在C ++中,你最终可以避免这种情况,只是因为编译器会为你提供一个成员方式的赋值运算符实现。

您无法复制this整体的Java限制虽然有点人为。 你可以通过成员编写获得完全相同的结果,但语言只是没有一种自然的方式来指定这样的操作 – C ++语法, (*this)不在Java中有类似的东西。 而且,事实上,Java中没有内置操作可以重新分配任何现有对象的内容 – 即使它不被称为this [对于基于堆栈的对象,这种操作可能更为重要,例如在C ++中很常见。


关于执行深层复制的用例: 它在Java中很复杂 。

对于C ++,一种面向值的类型语言。 赋值的语义意图通常是显而易见的。 如果我说a=b ,我通常希望a成为b独立克隆 ,包含相等的C ++会自动执行此操作以进行分配 ,并且还计划自动执行该过程,以进行比较。

对于Java和其他面向参考的语言,在一般意义上复制对象具有模糊的含义。 除了基元之外, Java不区分值类型和引用类型 ,因此复制对象必须考虑每个嵌套的类类型成员(包括父类的成员),并根据具体情况决定是否应该复制或仅引用该成员对象。 如果留给默认实现,很有可能结果不是你想要的。 比较 Java中的相等对象会遇到相同的歧义 。

基于所有这些, 基本问题的答案为什么我不能通过一些简单的,自动生成的操作来复制一个对象从根本上说,Java没有一个明确的概念,它意味着复制什么一个对象


最后一点,回答字面问题:

决定不将变量作为变量的原因是什么?

这样做毫无意义。 这个值只是一个已经传递给函数的指针,如果你能够改变它的值,它就不能直接影响用于调用该方法的任何对象或引用。 毕竟,Java是按值传递的。

在C ++中分配给*this并不等同于在Java中分配this 。 分配this ,并且在任何一种语言中都是不合法的。