Недопустимая ошибка прямой ссылки для статических полей final - PullRequest
12 голосов
/ 23 ноября 2011

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

java.util.concurrent.Callable и множество вариантов использования Object просто используются в качестве заполнителей для удаления ненужных фрагментов кода.

public class Test {
    static final Object foo = method(new java.util.concurrent.Callable<Object>() {
        @Override
        public Object call() throws Exception {
            return bar;
        }
    });

    static final Object bar = foo;

    static Object method(Object binder) {
        return null;
    }
}

При компиляции с использованием javac Test.java javac печатает следующее сообщение об ошибке:

Test.java:9: illegal forward reference
    static final Object bar = foo;
                              ^

Таким образом, компилятор жалуется на объявление bar, ссылающееся на foo, в то время как foo должно находиться в области объявления bar. Но как только ссылка на bar в объявлении foo будет удалена, например, изменив строку 5 с return bar; на return null;, класс будет принят компилятором.

Как это можно объяснить? Мое понимание forward как значения лексически после неверно или это какой-то особый случай, о котором я не знаю?

Ответы [ 2 ]

16 голосов
/ 23 ноября 2011

Ваше понимание прямая ссылка верно.Ссылка на foo в строке 9 вовсе не является прямой ссылкой , так как она не появляется в текстовом виде до ее объявления (см. Определение того, что составляет прямая ссылка враздел 8.3.2.3 из Спецификация языка Java ).

Наблюдаемое вами поведение является признаком ошибки javac .См. этот отчет об ошибке .Эта проблема, похоже, исправлена ​​в более новых версиях компилятора, например, OpenJDK 7 .

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

Обратите внимание, что ссылка на bar в call() является допустимой прямой ссылкой , поскольку она встречается внутри другого класса (см. примеры в разделе 8.3.2.3 Спецификация языка Java ).

Также обратите внимание, что каждое из следующих изменений приводит к исчезновению ошибки:

Создание barне финальный:

static Object bar = foo;

Инициализация bar в статическом или экземпляре инициализатора:

static final Object bar;

static {
  bar = foo;
}

Перенос инициализации foo в блок инициализации также помогает.

Инициализация bar из не окончательной временной ссылки на foo:

static Object tmp = foo;
static final Object bar = tmp;

Инициализация bar с Test.foo (найдена Томом Андерсоном) или с this.foo в нестатический регистр:

static final Object bar = Test.foo;

Удаление bar и обращение к объекту с использованием foo внутри call():

static final Object foo = method(new java.util.concurrent.Callable<Object>() {
    @Override
    public Object call() throws Exception {
        return foo;
    }   
});
1 голос
/ 23 ноября 2011

В Спецификациях языка Java конкретно упоминаются ограничения на поля объектов на этапе инициализации, в частности ( C - это интерфейс или класс):

Ошибка времени компиляции для прямых ссылок возникает при выполнении следующих условий:

  • Использование происходит в экземпляре (соответственно статической) переменной инициализатора C или в экземпляре (соответственно статической) инициализатора C .
  • Использование не в левой части назначения.
  • Использование через простое имя.
  • C является внутренним классом или интерфейсом, включающим использование.

Статья Что такое правила прямой ссылки? содержит отличное объяснение правил и ограничений, когда речь идет об инициализации членов и прямой ссылке.

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