Почему назначение на «это» не разрешено в Java? - PullRequest
19 голосов
/ 02 ноября 2011

Ошибка, которую я получаю от компилятора: «Левая часть присваивания должна быть переменной». Мой вариант использования - глубокое копирование, но на самом деле он не актуален.

В C ++ можно присвоить *this.

Вопрос не в том, как обойти присвоение this. Это очень просто, но, скорее, какое обоснование стоит за решением не делать this переменной.

Являются ли причины техническими или концептуальными?

До сих пор я догадывался - возможность перестройки Объекта случайным способом подвержена ошибкам (концептуальная), но технически возможна.

РЕДАКТИРОВАТЬ Пожалуйста, воздержитесь от вариаций "потому что в спецификации Java так сказано". Я хотел бы знать причину решения

Ответы [ 7 ]

12 голосов
/ 02 ноября 2011

В C ++ можно присвоить *this

Да, но вы не можете сделать this = something в C ++, что, на самом деле, я считаю, более близко соответствует тому, о чем вы спрашиваете здесь на стороне Java.

[...] каково обоснование решения не делать this переменной.

Я бы сказал, ясность / читабельность.

this было выбрано как зарезервированное слово, вероятно, поскольку оно не передается в качестве явного аргумента методу. Использование его в качестве обычного параметра и возможность переназначения ему нового значения может сильно испортить читаемость.

На самом деле, многие люди утверждают, что вам вообще не следует менять переменные-аргументы по этой самой причине.

Причины технические или концептуальные?

В основном концептуально, я бы предположил. Однако возникнет несколько технических причуд. Если бы вы могли переназначить значение на this, вы могли бы полностью скрыть переменные экземпляра за локальными переменными, например.

До сих пор мое предположение - возможность восстановления объекта случайным способом подвержена ошибкам (концептуальная), но технически возможна.

Я не уверен, что полностью понимаю это утверждение, но да, склонность к ошибкам, вероятно, является основной причиной решения сделать его ключевым словом, а не переменной.

5 голосов
/ 02 ноября 2011

Причины технические или концептуальные?

ИМО, концептуальное.

Ключевое слово 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 2 ноября '11 в 12: 18

При условии, что xy имеет правильный тип, ссылка на него будет установлена ​​на xy, , что делает «оригинальный» объект gc-приемлемым - kostja Nov 2 '11 в 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.

4 голосов
/ 02 ноября 2011

, поскольку this - это final,

this - ключевое слово, а не переменная.и вы не можете назначить что-то на ключевое слово.Теперь на минутку рассмотрим , если это была ссылочная переменная в спецификации проекта ... и посмотрите пример ниже

, и она содержит неявную ссылку на метод вызова объекта.и он используется только для справочных целей, теперь рассмотрим, что вы назначаете что-то для this, поэтому не будет ли это все нарушено?

Пример

рассмотрим следующий код изString class (Примечание: приведенный ниже код содержит ошибку компиляции, это просто демонстрация 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);
   }
3 голосов
/ 02 ноября 2011

this даже не переменная.Это ключевое слово, как определено в Спецификации языка Java :

При использовании в качестве основного выражения ключевое слово this обозначает значение, являющееся ссылкой на объект, для которогоМетод экземпляра был вызван (§15.12) или строящемуся объекту

Итак, это невозможно, так как невозможно присвоить значение while.

1 голос
/ 23 мая 2018

Присвоение (*this) в C ++ выполняет операцию копирования - обрабатывает объект как тип значения .

Java не использует концепцию типа значения для классов. Назначение объекта всегда по ссылке .

Чтобы скопировать объект, как если бы он был типом значения: Как скопировать объект в Java?


Терминология, используемая для Java, сбивает с толку: Является ли Java «передачей по ссылке» или «передачей по значению»

Ответ: Java передает ссылки по значению. здесь )

Другими словами, поскольку Java никогда не рассматривает не примитивы как типы значений, каждая переменная типа класса является ссылкой (фактически указателем).

Поэтому, когда я говорю, «назначение объекта всегда по ссылке», это может быть технически более точным для фразы, что «назначение объекта всегда значение ссылка».

Практическое значение различия, проведенного Java всегда , являющегося передачей по значению, воплощено в вопросе " Как мне сделать функцию свопинга в Java? ", и его ответ: Вы не можете . Такие языки, как C и C ++, могут предоставлять функции подкачки, потому что они, в отличие от Java, позволяют назначать из любую переменную , используя ссылку на эту переменную, что позволяет вам изменять ее значение (если не -const) без изменения содержимого объекта, на который он ранее ссылался.

Это может заставить вашу голову кружиться, пытаясь обдумать это до конца, но здесь ничего не происходит ...

  • Переменные типа класса Java всегда являются "ссылками", которые фактически являются указателями.
  • Java-указатели являются примитивными типами.
  • Назначение Java всегда по значению базового примитива (в данном случае указатель).
  • У Java просто нет механизма, эквивалентного передаче по ссылке C / C ++, который позволял бы вам косвенно изменять автономный примитивный тип, который может быть «указателем», таким как this.

Кроме того, интересно отметить, что C ++ на самом деле имеет два разных синтаксиса для передачи по ссылке. Один основан на явных указателях и был унаследован от языка Си. Другой основан на операторе C ++ ссылочного типа &. [Существует также форма управления ссылками C ++ smart pointer , но это больше похоже на семантику в стиле Java - где сами ссылки передаются по значению.]

Примечание. В приведенном выше обсуждении assign-by и pass-by обычно являются взаимозаменяемыми. В основе любого назначения лежит концептуальная операторная функция , которая выполняет назначение на основе правого объекта, являющегося пройденным дюйм.


Итак, возвращаясь к исходному вопросу : Если бы вы могли присвоить this в Java, это означало бы изменение значения ссылки, хранящейся в this. Это фактически эквивалентно прямому присвоению this в C ++, что также недопустимо в этом языке.

Как в Java, так и в C ++, this фактически является указателем, который нельзя изменить. Java кажется другим, потому что он использует оператор . для разыменования указателя - что, если вы привыкли к синтаксису C ++, создает впечатление, что это не один.

Вы можете, конечно, написать что-то на Java, похожее на конструктор копирования C ++ , но в отличие от C ++, нет способа обойти тот факт, что реализация должна быть предоставлена ​​с точки зрения явной инициализации по элементам. [В C ++ вы можете избежать этого, в конечном счете, только потому, что компилятор предоставит вам для каждого члена реализацию оператора присваивания .]

ThОграничение Java, которое вы не можете скопировать в this в целом, является искусственным. Вы можете достичь точно такого же результата, написав его для каждого члена, но у языка просто нет естественного способа указать такую ​​операцию, которая будет выполняться с this - синтаксис C ++ , (*this) не имеет аналога в Java. И, фактически, в Java нет встроенной операции, которая переназначает содержимое любого существующего объекта - даже если он не упоминается как this. [Такая операция, вероятно, более важна для основанные на стеке объекты, такие как обычные в C ++.]


Относительно сценария использования глубокого копирования : Это сложно в Java .

Для C ++ - язык, ориентированный на значения. Семантическое намерение присваивания в целом очевидно. Если я говорю a=b, я обычно хочу, чтобы a стал независимым клоном из b, содержащим равное значение . C ++ делает это автоматически для присваивания , и планирует автоматизировать процесс, а также для сравнения.

Для Java и других ссылочно-ориентированных языков копирование объекта в общем смысле имеет неоднозначное значение. За исключением примитивов, Java не делает различий между типами значений и ссылочными типами , поэтому при копировании объекта необходимо учитывать каждый вложенный член типа класса (включая родительский) и принимать решение по регистру. в каждом конкретном случае, если этот объект-член должен быть скопирован или просто указан. Если оставить реализации по умолчанию, очень вероятно, что результат будет не тем, что вы хотите. Сравнение объектов на равенство в Java страдает от одинаковых неоднозначностей .

Исходя из всего этого, ответ на основной вопрос : почему я не могу скопировать объект с помощью простой, автоматически сгенерированной операции на this, что по сути, у Java нет четкого представления о том, что означает копирование объекта .


Еще один момент, чтобы ответить на буквальный вопрос:

Каково обоснование решения не делать this переменной?

Было бы просто бессмысленно это делать. Значение this - это просто указатель, который был передан функции, и если вы смогли изменить значение this, это не могло напрямую повлиять на какой-либо объект или ссылку, использованную для вызова этого метода. В конце концов, Java передается по значению.

1 голос
/ 02 ноября 2011

this в Java - это часть языка, ключевое слово, а не простая переменная.Он был создан для доступа к объекту из одного из его методов, а не из другого объекта.Присвоение ему другого объекта может привести к путанице.Если вы хотите сохранить ссылку на другой объект в своем объекте, просто создайте новую переменную.

Причина только концептуальная.this было сделано для доступа к самому объекту, например, чтобы вернуть его в метод.Как я уже сказал, это приведет к путанице, если вы назначите другую ссылку на него.Скажите мне причину, по которой изменение this имело бы смысл.

0 голосов
/ 03 ноября 2011

Назначение *this в C ++ не эквивалентно назначению this в Java.Назначение this есть, и это не разрешено ни на одном языке.

...