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

时间:2011-11-02 11:32:11

标签: java reference this variable-assignment

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

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

问题不在于如何规避this的作业。这很简单,但是决定不让this成为变量的背后有什么理由。

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

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

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

7 个答案:

答案 0 :(得分:12)

  

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

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

  

[...]决定不将this变为变量的背后有什么理由。

我会说清晰度/可读性。

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

事实上,很多人认为你根本不应该改变论证变量,原因就在于此。

  

技术或概念上的原因是什么?

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

  

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

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

答案 1 :(得分:4)

因为thisfinal

this是关键字,而不是变量。并且您无法为关键字分配内容。现在一分钟考虑 if 它是设计规范中的参考变量..并参见下面的例子

并且它包含对象调用方法的隐式引用。它仅用于参考目的,现在考虑为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);
   }

答案 2 :(得分:4)

  

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

IMO,概念性。

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

由于this改变没有意义,因此将其变为变量是没有意义的。

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


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

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

  • 您如何处理互斥锁?例如,如果您在同步方法的中间分配this会发生什么......并且提议的语义是否有意义? (问题是你最终在同步方法中对你没有锁定的对象执行...或者你必须解锁旧的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)


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

我注意到你评论过:

  

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

     

如果xy属于正确类型,则此引用将设置为xy将“原始”对象设为gc-eligible - kostja Nov 2' 11点12:24

那不行。

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

  2. 即使它确实如此,那只是举行参考的地方。除非在所有地方分配null,否则该对象无法进行垃圾收集。

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

    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的好用例。

答案 3 :(得分:3)

this甚至不是变量。它是一个关键字,如Java Language Specification中所定义:

  

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

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

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

要复制对象,就好像它是值类型:How do I copy an object in Java?

用于Java的术语令人困惑:Is Java “pass-by-reference” or “pass-by-value”

答案: Java按值传递引用。(来自here

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

所以,当我说'"对象分配总是按引用"时,在短语中可能更准确,因为"对象分配始终是的值参考"。

Java 总是所传达的区别的实际含义体现在问题" How do I make my swap function in java?"及其答案:{ {3}}。诸如C和C ++之类的语言能够提供交换功能,因为它们与Java不同,允许您通过使用对该变量的引用从任何变量进行分配 - 从而允许您更改其值(如果non-const)而不改变它先前引用的对象的内容。

它可能让你的头脑旋转,试图一直想到这一点,但这里什么都没有......

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

另外,值得注意的是,C ++实际上有两种不同的语法,用于传递引用。一个基于显式指针,并且继承自C语言。另一个基于C ++ You can't。 [还有C ++ 智能指针形式的参考管理,但这更类似于类似Java的语义 - 其中引用本身是按值传递的。]

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

所以回到最初的问题:如果您可以在Java中分配给this,那么这意味着更改this所持有的引用的值。这实际上相当于在C ++中直接分配给this,这在该语言中也是不合法的。

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

可以,当然,用Java编写类似于C ++ reference-type operator &的东西,但与C ++不同,没有办法解决实现将会需要根据明确的成员初始化提供。 [在C ++中,你可以避免这种情况,最终只是因为编译器会为你提供赋值运算符的成员实现。]

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

关于执行深层复制的用例:copy constructor

对于C ++,一种面向价值类型的语言。赋值的语义意图通常是显而易见的。如果我说a,我通常希望b成为this的独立It's complicated in Java,包含相等的 C ++会自动执行此操作以进行分配,还有clone可自动执行此过程,也可用于比较。

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

基于所有这些,基本问题的答案为什么我无法通过this上的一些简单的,自动生成的操作来复制对象,就是,从根本上说,Java并不清楚复制对象意味着什么

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

  

决定不将this变为变量的原因背后有什么理由?

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

答案 6 :(得分:0)

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