Константные выражения Java и устранение кода

Как обсуждалось здесь, javac и другие компиляторы Java могут предоставлять возможности устранения кода для if-операторов, где условие представляет собой "Constant Expression"< /а>.

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

Например, предположим, что у меня есть следующие классы в соответствующих указанных пакетах:

package foo;

public class Foo {
    public static final boolean CONDITION = false;
}

и

package bar;

import foo.Foo;

public class Bar {
    public void test() {
        if (Foo.CONDITION) {
            System.out.println("This line of code could be eliminated.");
        } else {
            System.out.println("This line of code will be executed.");
        }
    }
}

Понятно, что если foo-пакет загружается во время выполнения из внешнего jar-файла, технически компилятор не может просто предположить, что Foo.CONDITION будет ложным, и не должен исключать true-ветвь оператора if.

Принимая во внимание, что если бы Foo и Bar были на самом деле в одном пакете, то ветвь true определенно должна быть исключена (если компилятор вообще поддерживает исключение кода).

Не совсем уверен, как лучше сформулировать этот вопрос, но: насколько «близко» должно быть Foo к Bar, чтобы постоянное выражение в Foo также считалось постоянным в Bar? Должны ли они быть в одном файле? такой же пакет? тот же jar-файл? или это вообще не имеет значения (т. е. будет ли компилятор всегда рассматривать Foo.CONDITION как константу и использовать значение, найденное в пути сборки, во время компиляции)?


comment
Попробуйте посмотреть байт-код с помощью javap -c Bar.   -  person arshajii    schedule 23.05.2014
comment
@arshajii Действительно, это позволило бы мне проверить. Но это дало бы мне только одну точку данных о поведении именно того компилятора, который я использую. Может быть, есть реальная спецификация, которая отвечает на этот вопрос? Я не мог найти ни одного...   -  person Markus A.    schedule 23.05.2014


Ответы (2)


Поскольку это часть общедоступной «подписи» класса, на который делается ссылка, предполагается, что он является постоянным. По сути, идея состоит в том, что final означает final, и если вы измените его, это ваша вина.

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

Обновление: на самом деле JLS дает еще более надежная гарантия:

Если поле является постоянной переменной (§4.12.4), то удаление ключевого слова final или изменение его значения не нарушит совместимость с уже существующими двоичными файлами, заставив их не запускаться, но они не увидят никакого нового значения для использования поля, если они не перекомпилированы. Это верно, даже если само использование не является константным выражением времени компиляции (§15.28).

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

person biziclop    schedule 22.05.2014
comment
Идеальный! Этот раздел JLS очень четко отвечает на вопрос! Вот собственно ссылка на него: docs.oracle.com/javase/specs/jls/se7/html/ (для ясности там есть отличные примеры кода) Спасибо! √ +1 - person Markus A.; 23.05.2014

Неважно, насколько близка ваша константа. Если это константа времени компиляции, как определено в спецификация, она будет встроена. Статический финал (константы) встроен в спецификацию Java (хотя я тоже не нашел спецификацию). Это В статье о мире Java эта тема обсуждается более подробно. Соответствующие вещи:

Согласно Спецификации языка Java, любое статическое конечное поле, инициализированное выражением, которое может быть оценено во время компиляции, должно быть скомпилировано в байтовый код, который «встраивает» значение поля. То есть внутри класса Main не будет динамической ссылки, сообщающей ему о получении значения для A от InterfaceA во время выполнения. Вместо этого литерал 1 будет напрямую компилироваться в Main.main().

person Martin Serrano    schedule 22.05.2014
comment
Интересным побочным эффектом этого является то, что в зависимости от выражения, на которое вы ссылаетесь, оно может запускать или не запускать загрузку ссылочного класса во время выполнения. Что несколько пугает. - person biziclop; 23.05.2014
comment
Эта ссылка очень полезна! Благодарю вас! Интересный способ разрешения циклических зависимостей. Сообщение об ошибке, вероятно, было бы более полезным в этом случае (как это делает Excel), а не несколько случайная зависимость от порядка оценки. +1 - person Markus A.; 23.05.2014
comment
Этот один также рассказывает интересную историю о том, что может происходит, когда несколько библиотек (построенных непоследовательно) встраивают константу. - person Martin Serrano; 23.05.2014