Неиспользуемые ссылочные переменные всегда записываются в Java - PullRequest
0 голосов
/ 08 ноября 2018

Я хотел бы знать, если это деталь реализации ...

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

Похоже, однако, что любая локальная переменная, на которую ссылаются, перехватывается, даже если она не используется для обновления 181 Oracle JDK 8.

public static void main(String[] args) {
    Thread t = Thread.currentThread();
    Runnable run = new Runnable() {
        @Override
        public void run() {
            t.yield();
        }
    };
    Runnable run2 = () -> t.yield();
    run.run();
    run2.run();
}

Байт-код для анонима Runnable -

  // access flags 0x1
  public run()V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    GETFIELD UnusedLocalVariable$1.val$t : Ljava/lang/Thread;
    POP
    INVOKESTATIC java/lang/Thread.yield ()V
   L1
    LINENUMBER 9 L1
    RETURN
   L2
    LOCALVARIABLE this LUnusedLocalVariable$1; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 1

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

Лямбда делает то же самое, также захватывая переменную.

Это всегда так, или детали реализации?

1 Ответ

0 голосов
/ 08 ноября 2018

Спецификация не делает различий между «ссылками» и «использованием» переменной. В связи с этим вы используете переменную, вызывая t.yield(), несмотря на то, что вы вызываете метод static. Для этого сценария в спецификации написано

JLS § 15.12.4.1, 15.12.4.1. Расчет целевого задания (при необходимости) :
  • Если форма ExpressionName. [TypeArguments] Идентификатор , затем:
    • Если режим вызова static, то целевой ссылки нет. ExpressionName оценивается, но результат затем отбрасывается.
    • В противном случае целевой ссылкой является значение, обозначаемое ExpressionName .

, поэтому поведение соответствует спецификации.

Хотя очевидно, что оценка должна произойти, когда она имеет побочные эффекты, я бы не пошел так далеко, чтобы заключить, что последовательность байт-кодов ALOAD 0, GETFIELD, POP строго требуется для выполнения формального правила, согласно которому переменная оценивается, а результат отбрасывается, так как этот код не имеет никакого эффекта.

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

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

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

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