Gem Notification Notification Gem и Rails 3

Статический компилятор Java (javac) включает некоторые статические конечные переменные и переносит значения непосредственно в пул констант. Рассмотрим следующий пример. Класс A определяет некоторые константы (public static final) переменные):

public class A {
    public static final int INT_VALUE = 1000;
    public static final String STRING_VALUE = "foo";
}

Класс B использует следующие константы:

public class B {
    public static void main(String[] args) {
        int i = A.INT_VALUE;
        System.out.println(i);
        String s = A.STRING_VALUE;
        System.out.println(s);
    }
}

Когда вы компилируете класс B, javac получает значения этих констант из класса A и вставляет эти значения в B.class. В результате зависимость B должна была A во время компиляции стирается из байт-кода. Это довольно своеобразное поведение, потому что вы запекаете значения этих констант во время компиляции . И вы могли бы подумать, что это одна из самых простых вещей, которую JIT-компилятор может делать во время выполнения.

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

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

53
задан Lyubomyr Shaydariv 13 January 2017 в 01:09
поделиться

5 ответов

В правиле 93 Java Puzzlers (Джошуа Блох) говорится, что вы можете обойти это, не допуская, чтобы конечное значение считалось константой. Например:

public class A {
  public static final int INT_VALUE = Integer.valueOf(1000).intValue();
  public static final String STRING_VALUE = "foo".toString();
}

Конечно, все это не имеет значения, если у вас нет доступа к коду, определяющему константы.

44
ответ дан 7 November 2019 в 08:47
поделиться

Я так не считаю. Простейшим обходным решением было бы представить их как свойства, а не поля:

public class A {
    private static final int INT_VALUE = 1000;
    private static final String STRING_VALUE = "foo";

    public static int getIntValue() {
        return INT_VALUE;
    }
    public static String getStringValue() {
        return STRING_VALUE;
    }
}

Не забывайте, что в некоторых случаях инлайнинг необходим для использования значения - например, если вы хотите использовать INT_VALUE в качестве case в блоке switch, то должен быть указан как постоянное значение.

13
ответ дан 7 November 2019 в 08:47
поделиться

JLS 13.4.9 решает эту проблему. Их рекомендация - в основном избегать констант времени компиляции, если значение каким-либо образом может измениться.

(Одна из причин, по которой требуется встраивание константы - это операторы переключения требовать константы для каждого случая, и нет два таких постоянных значения могут быть такой же. Компилятор проверяет наличие повторяющиеся постоянные значения в переключателе заявление во время компиляции; класс формат файла не является символическим связь значений регистра.)

Лучший способ избежать проблем с «непостоянные константы» в широко распространенный код должен объявить только значения констант времени компиляции которые действительно вряд ли когда-либо изменять. Кроме истины математические константы, мы рекомендуем этот исходный код очень экономно используется объявленных переменных класса статический и окончательный. Если только для чтения характер финала обязателен, лучше выбор - объявить частный статический переменная и подходящий аксессуар метод, чтобы получить его значение.Таким образом, мы рекомендую:

 private static int N;
общедоступный статический int getN () {вернуть N; }

, а не:

 public static final int N = ...;

Нет проблем с:

 public static int N = ...;

, если N не должен быть доступен только для чтения.

7
ответ дан 7 November 2019 в 08:47
поделиться

Чтобы остановить встраивание, вам нужно сделать значения неконстантами времени компиляции (термин JLS). Вы можете сделать это без использования функций и создания минимального байт-кода, используя null в выражении инициализатора.

public static final int INT_VALUE = null!=null?0: 1000;

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

9
ответ дан 7 November 2019 в 08:47
поделиться

Я чувствую, что java сильно полагается на динамическую компиляцию и не делает никакой причудливой логики компиляции, как в C++.

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

В стандартном javac вы не сможете воспользоваться этой опцией. 1. какой-то тип графа зависимостей, например extends или implements 2. использовать связывание на основе методов.

-s

-2
ответ дан 7 November 2019 в 08:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: