как внутренний класс делает эту ссылку побег - PullRequest
0 голосов
/ 01 ноября 2019

пример несовместимого кода раздел "внутренний класс"

кажется, что во внутреннем классе не вызывается метод экземпляра, поэтому я не знаю, как эта ссылка избежала в этомфрагмент кода

Ответы [ 3 ]

1 голос
/ 01 ноября 2019

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

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

Надеемся, что это демонстрирует потенциальную проблему:

class DefaultExceptionReporter implements ExceptionReporter {

    private final int foo;

    public DefaultExceptionReporter(ExceptionReporter er) {
        er.setExceptionReporter(new ExceptionReporter() {

            {
                System.out.println(DefaultExceptionReporter.this.foo);
            }

            public void report(Throwable t) {}
            public void setExceptionReporter(ExceptionReporter er) {}
        });
        foo = 1;
    }

    // ...
}

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

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

0 голосов
/ 01 ноября 2019

Технически в этом примере вы разрешаете this ссылку на escape.

Давайте представим, что:

  • какой-то другой поток имеет доступ к экземпляру ExceptionReporter, который передается в конструктор DefaultExceptionReporter
  • , к которому вы фактически обращаетесьвнешний экземпляр через DefaultExceptionReporter.this в методах вашего экземпляра анонимного класса (например, вы хотите получить доступ к полю или вызвать метод)

В этом случае другой поток может вызывать методы переданного ExceptionReporterконструктору DefaultExceptionReporter. И если эти методы вызывают методы из экземпляра, установленного через setExceptionReporter - другой поток потенциально может получить доступ к DefaultExceptionReporter до того, как его экземпляр будет полностью создан. Доступ из других Thread может осуществляться через цепочку ссылок:

ExceptionReporter (передается в конструктор) -> ExceptionReporter (анонимно) -> DefaultExceptionReporter.this.

0 голосов
/ 01 ноября 2019

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

public static void main(String[] args) {
    ExceptionReporter rep = new DefaultExceptionReporter(new ExceptionReporter() {
        @Override
        public void setExceptionReporter(ExceptionReporter er) {
            for (Field field : er.getClass().getDeclaredFields()) {
                System.out.println(field);
            }
        }

        @Override
        public void report() { }
    });
}

Вывод:

final my.package.DefaultExceptionReporter my.package.DefaultExceptionReporter$1.this$0

Это потому, что анонимный класс всегданестатический внутренний класс ( см. ссылку ), и эти виды классов всегда имеют неявную this ссылку на включающий класс.

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