Передает 'this' в принятой практике вызова метода в java

93

Это хорошая / плохая / приемлемая практика - передавать текущий объект в вызове метода. Как в:

public class Bar{
    public Bar(){}

    public void foo(Baz baz){
        //  modify some values of baz
    }
}

public class Baz{
    //constructor omitted

    public void method(){
        Bar bar = new Bar();
        bar.foo(this);
    }
}

В частности, bar.foo(this)приемлема ли линия ?

maxf130
источник
60
Почему это неприемлемо? Это обычное дело.
Denys Séguret
3
Итак ... это 8 "да" :) (И да, я постоянно обновляю его.)
Alex Gittemeier
2
нестатический анонимный класс автоматически передает эту ссылку суперкласса, поэтому это будет приемлемо. Единственное предостережение - осторожность с циклическими ссылками.
Mehul Rathod
5
Однако есть одно предостережение: вы не должны передавать это в конструктор, потому что это приведет к тому, что ваш объект окажется в несогласованном состоянии. Люди обычно делают это, когда создают обратные вызовы (например, ActionListener) как анонимные внутренние классы, а затем передают это другому объекту.
Tamas Rev
3
@dystroy, хотя я согласен, что это приемлемо, подразумевая, что это приемлемо, потому что это распространено, - действительно плохая логика. Делать что-то, потому что это обычное дело, может доставить вам массу неприятностей
Кэрри Кендалл

Ответы:

155

Нет причин не использовать его, thisэто текущий экземпляр, и его вполне законно использовать. На самом деле часто нет четкого способа пропустить это.

Так что используйте это.

Поскольку трудно убедить, что это приемлемо без примера (отрицательный ответ на такой вопрос всегда легче аргументировать), я просто открыл один из наиболее распространенных java.langклассов String, и, конечно же, я нашел примеры этого использования, например

1084        // Argument is a String
1085        if (cs.equals(this))
1086            return true;

Ищите (thisв больших «принятых» проектах, не преминете найти.

Дени Сегюре
источник
6
Прекрасный ответ.
maxf130
15
-1, потому что нет упоминания о том, что двунаправленные отношения классов более сложны, чем однонаправленные. Очень важно, чтобы программное обеспечение было максимально понятным. В приведенном выше конкретном примере было бы разумнее переместить метод foo в класс Baz, чтобы избежать двунаправленной ссылки между двумя классами и объединить поведение и данные.
JW.
35
-1 к ответу, потому что вы нашли в вопросе дополнительную мелочь, которую вы можете прокомментировать? В самом деле ?
Denys Séguret
13
@dystroy Да, я не согласен с вашей вступительной фразой: «Нет причин не использовать это».
JW.
4
На практике в реальном коде (в отличие от упрощенного примера OP) передача thisне означает, что вы добавляете двунаправленную ссылку, например, из-за наследования и интерфейсов.
Denys Séguret
165

В этом нет ничего плохого. Что НЕ является хорошей практикой, так это делать то же самое внутри конструкторов, потому что вы дадите ссылку на еще не полностью инициализированный объект.

Здесь есть что-то вроде похожего поста: Java пропускает это в конструкторе, где они объясняют, почему последнее - плохая практика.

моргано
источник
18
+1: хорошо, чтобы указать на опасность обращения к «this» в конструкторах.
Вирсавия
Это не обязательно плохая практика. Например, Carконструктор может создавать Wheelэкземпляры, при этом объект Carбез Wheelобъекта будет инициализирован не полностью, а объект Wheelбез соответствующего Carобъекта также будет инициализирован не полностью. В этом случае в конструкторе Car может быть допустимо перейти thisк конструктору Wheel. Другой альтернативой было бы сделать и Car, и Wheel иметь частный конструктор и использовать фабричную функцию, которая конструирует Car, Wheel и устанавливает Wheel на Автомобиль; но должен ли это быть статический метод для Car или статический метод для Wheel?
Lie Ryan
Очевидно, вам следует создать программу, CarFactoryWheelInstallerProxyкоторая устанавливает колеса за вас.
Кевин
6
@LieRyan Wheelполностью подчиняется Car, и ИМО не должна знать об этом Car.
Izkata
3
ЕДИНСТВЕННЫЙ недостаток использования thisизнутри конструктора заключается в том, что thisон передается в метод или контекст, из которого еще не полностью построенная ссылка на объект публикуется для ненадежных или неизвестных клиентов (или клиентского кода, который предполагает, что он имеет представление о полностью построенный объект). На thisмой взгляд, переход от конструктора к частному для пакета методу, который выполняет общую инициализацию, не только приемлем, но и желателен.
scottb
42

Да , но вы должны быть осторожны с двумя вещами

  1. Передача этого , когда объект не был построен еще (то есть в его конструкторе)
  2. Передача this в долгоживущий объект, который сохранит ссылку в живых и предотвратит сборку мусора для этого объекта.
Стефанос Т.
источник
1
Обратите внимание, что конструктор в Java на самом деле не является конструктором, вероятно, более уместно называть конструктор Java «инициализатором». В конструкторе Java объекту фактически была выделена память, этот объект фактически уже существует / создан в конструкторе.
Lie Ryan
1
Не совсем так, у объекта могут быть некоторые переменные экземпляра, которые еще не инициализированы, так что объект еще не работает полностью. Итак, в конструкторе вы можете передать это второму объекту, который может вызвать метод объекта, который еще не инициализировал все свои переменные экземпляра.
Stefanos T.
тем не менее, пока второй объект знает, что переданный объект не инициализирован, и рассматривает его как непрозрачный объект или вызывает только методы, которые были объявлены безопасными в таком состоянии, он не вызовет проблем с переходом thisк нему. Это было бы невозможно, если бы объект не был выделен.
Lie Ryan
13

Это совершенно нормально и вполне приемлемо.

Вирсавия
источник
5

это означает текущий объект. То, что вы делаете, систематически правильно, но я не вижу в этом необходимости, если вы вызываете метод в том же классе.

Джунед Ахсан
источник
2
В примере кода Baz.method () - это метод экземпляра, который вызывает Bar.foo () с экземпляром Baz в качестве параметра. Таким образом, OP не вызывает метод в том же классе.
пользователь
@ MichaelKjörling Я думаю, что Юнед говорит, что, переместив метод foo () в класс Baz, не будет необходимости переходить thisмежду двумя классами. Таким образом, нет необходимости добавлять дополнительную сложность.
JW.
4

Передавать текущий объект в вызове метода - плохая практика, если есть менее сложные альтернативы для достижения того же поведения.

По определению, двунаправленная ассоциация создается, как только thisпередается от одного объекта к другому.

Процитируем рефакторинг Мартина Фаулера:

Измените двунаправленную ассоциацию на однонаправленную (200)

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

...

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

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

Также часто бывает необходимо временно испортить ваш дизайн, выполняя «плохие практики», во время более длительного рефакторинга вашего кода для общего улучшения. (Шаг назад, два шага вперед).

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

JW.
источник
Вы путаете упрощенный пример с необходимостью установить двунаправленную ссылку. Передача этого параметра в качестве параметра, что должно быть ясно из многих примеров исходного кода java.lang (например, того, который вы видели в моем ответе), не означает, что вы добавляете двунаправленную зависимость. На мой взгляд, этот ответ должен был быть комментарием.
Denys Séguret
@dystroy Спасибо, что добавили комментарий, чтобы объяснить, почему вы проголосовали против. Всегда полезно знать. Я изменю свой ответ, чтобы уточнить, что, по определению, двунаправленная ассоциация создается сразу после thisпередачи.
JW.
1
"по определению двунаправленная ассоциация создается, как только она передается" . Это проясняет, где вы не понимаете. Посмотрите на пример, который я привожу. Двунаправленной ссылки нет, потому что тип аргумента equals- Object. Это очень распространено: принимающий метод определяет свой аргумент как более общий класс или как интерфейс. Одна из причин, по которой такой шаблон используется в Java, - избежать нежелательных зависимостей. Прежде чем продолжить, я бы посоветовал вам взглянуть на многие случаи передачи в thisкачестве аргумента в уважаемых java-библиотеках.
Denys Séguret
Давайте просто согласимся не соглашаться. Моя жизнь стала намного проще, поскольку я по возможности избегал передачи thisкода. Я бы порекомендовал сделать это другим.
JW.
2
@JW: рассуждения, приведенные в этом ответе, неуместны. Всегда плохая идея делать X, если есть что-то попроще, для любого значения X.
Ли Райан
4

Да. вы можете использовать его. Это обычное дело в программировании. Но у его thisиспользования есть свои плюсы и минусы. Тем не менее, это не опасно.

Суреш Атта
источник
Побочных эффектов масса. Это добавляет сложности.
JW.
Если есть столько побочных эффектов, мы не смогли бы найти ни одного подобного доказательства в нашем исходном коде Java. См. Пример @destroys из исходного кода.
Суреш Атта
2

Просто чтобы добавить еще один пример, когда прохождение thisправильное и следует хорошему дизайну: шаблон посетителя . В шаблоне проектирования Visitor метод accept(Visitor v)обычно реализуется в виде простого вызова v.visit(this).

Петр Зеленка
источник
1

Приемлемый

Фрагмент из документов Oracle JAVA:

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

Использование этого с полем

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

Наргис
источник
2
«Вы можете ссылаться на любой член текущего объекта » - это, похоже, не отвечает на вопрос «допустимо ли передавать thisв качестве параметра? ».
пользователь
2
Это говорит о том, как вы можете this.some_variableссылаться на переменную класса, а не на локальную переменную. Это не имеет ничего общего с передачей thisв качестве параметра.
Jose Salvatierra
0

Все в java передается по значению. Но объекты НИКОГДА не передаются в метод!
Когда java передает объект методу, она сначала создает копию ссылки на объект, а не копию самого объекта. Следовательно, это идеально используемый метод в java. И чаще всего следил за использованием.

блганеш101
источник
9
Это выглядит по теме.
Denys Séguret
4
«Все в java передается по значению». - это очень вводящий в заблуждение первоначальный комментарий. Фактически все объекты передаются по ссылке, а все примитивные типы передаются по значению. У вас НИКОГДА не было thisссылки на примитивный тип, и поэтому я думаю, что ваша «дополнительная информация» вносит путаницу.
Стюарт
1
@Stewart: Он сразу дает понять, что он не имеет в виду, что объекты копируются целиком.
LarsH
10
@Stewart нет, объекты не передаются по ссылке, а ссылки на объекты передаются по значению. Это важное различие - передача по ссылке будет означать, что если у меня есть локальная переменная, которая ссылается на объект, и передать эту переменную другому методу, метод сможет изменить, на какой объект ссылается моя переменная, и это определенно не что-то вы можете сделать на Java. Метод может изменять состояние объекта с помощью своей собственной копии ссылки, но он не может изменить мою копию ссылки, чтобы указать на что-то еще.
Ian Roberts
2
@Stewart yoda.arachsys.com/csharp/parameters.html - хорошая статья, в которой объясняется разница между передачей по ссылке и передачей ссылок по значению в контексте C #, который поддерживает и то, и другое.
Ian Roberts