Константни изрази на Java и премахване на код

Както е обсъдено тук, javac и други компилатори на Java може да предоставят възможности за елиминиране на код за if-операции, където условието е "Постоянен израз".

Как се отразява това, ако кодът ми използва постоянен израз, който зависи от други постоянни изрази, дефинирани в различни пакети?

Например, да кажем, че имам следните класове в съответните посочени пакети:

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 за константа и ще използва стойността, намерена в пътя на компилиране по време на компилиране)?


person Markus A.    schedule 22.05.2014    source източник
comment
Опитайте да погледнете байт кода с javap -c Bar.   -  person arshajii    schedule 23.05.2014
comment
@arshajii Наистина, това ще ми позволи да проверя. Но това ще ми даде само една точка от данни за поведението на точния компилатор, който използвам. Може би има действителна спецификация, която отговаря на този въпрос? Не можах да намеря такъв...   -  person Markus A.    schedule 23.05.2014


Отговори (2)


Тъй като е част от публичния "подпис" на посочения клас, се приема, че е постоянен. По същество идеята е, че 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 world статията обсъжда темата в някои подробности. Съответните неща:

Съгласно спецификацията на езика на 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