Могу ли я сослаться на объект в его конструкторе? - PullRequest
4 голосов
/ 21 апреля 2010

Могу ли я сделать следующее?

public Manager(String userName) {
    game = new Game(userName);
    game.addManager(this);
}

Проблема в том, что я ссылаюсь на объект (this) в его конструкторе (до того, как он был фактически создан).

Ответы [ 7 ]

4 голосов
/ 21 апреля 2010

Хотя это допустимая Java, и в случае, который вы описываете (где это последняя строка конструктора), это довольно безопасно (за исключением некоторых крайних случаев), а на практике это плохо делать, и как с помощью goto (в языках, поддерживающих ключевое слово) оно должно быть что-то вы думаете, долго и упорно о. В вашем случае лучше будет сделать конструктор закрытым, удалить вызов addManager и предоставить статический метод фабрики:

 public static Manager createManager(String userName) {
        Manager manager = new Manager(userName);
        manager.game.addManager(manager);
        return manager;
 }

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

3 голосов
/ 21 апреля 2010

Да, вы можете сделать это, но вы не должны этого делать .

Проблема в том, что публикация this во время работы конструктора может привести к всевозможным странным побочным эффектам, поскольку некоторые общие гарантии не выполняются, когда конструктор все еще работает (например, final переменные кажется, они меняют свое значение, пока конструктор еще работает).

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

(последний абзац был "украден" из одного из моих предыдущих ответов ).

2 голосов
/ 21 апреля 2010

Да, это прекрасно законно на Java, но не рекомендуется. Подробнее см. здесь в ключевом слове this.

1 голос
/ 21 апреля 2010

Как сказал @James, вы можете, но это не обязательно то, что вы хотите сделать. Если game.addManager пытается получить доступ к определенным свойствам диспетчера, вы можете попытаться получить доступ к свойствам диспетчера, которые еще не были инициализированы. Лучше подходить, чтобы внешний объект вызывал некоторый метод init (или метод жизненного цикла), чтобы добавить менеджер, а не делать это в конструкторе.

0 голосов
/ 21 апреля 2010

Мальчик, это не безопасно! Хоть действительный код, но не хороший дизайн! Ваш код позволяет "этой" ссылке сбежать до того, как объект будет правильно сконструирован.

Представьте, что game.addManager () вызовет некоторый метод xxx () для ссылки "this". И у нас есть подкласс Manager, ChildManager, который переопределяет метод xxx (), и этот метод зависит от поля в ChildManager (которое не инициализируется, когда супер-конструктор достигает последней строки кода). Game.addManager () будет видеть неинициализированное значение поля в ChildManager, что очень и очень опасно!

Пример кода:

    public class Manager {
    Game game;
    public Manager (String userName){
        game = new Game(userName);
        game.addManager(this);
    }
    public void xxx(){

    }
}

public class ChildManager extends Manager {
    String name;
    public ChildManager (String username){
        super(username);
        name = username;
    }

    public void xxx (){
        System.out.println(name);
    }
}

public class Game {
    public Game (String userName){

    }

    public void addManager (Manager m){
        m.xxx();
    }
}
0 голосов
/ 21 апреля 2010

Эта техника нарушает одну из концепций параллелизма Java - безопасную публикацию. Вы должны использовать init() метод для этой цели или другую технику.

Видите ли, вы можете инициализировать некоторые последние поля в своем конструкторе (или выполнить некоторую другую инициализацию) после того, как эта ссылка была экранирована. Если вы передаете экземпляр вашего объекта в его конструкторе другому объекту, вы можете получить обратный вызов во время конструирования. И это может привести к непоследовательному поведению, NPE, тупикам и т. Д.

0 голосов
/ 21 апреля 2010

посмотрим, поможет ли это, на самом деле это для c / c ++, но я думаю, что то же самое для java:

http://www.gotw.ca/publications/mill13.htm

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...