JAVA exPress logo Opublikowane na JAVA exPress (http://www.javaexpress.pl)

Express killers, cz. IV

Damian Szczepanik

Numer 5 (2009-10-01)

Przykład pierwszy

Na początek krótkie przypomnienie: jaki będzie wynik uruchomienia poniższego kawałka kodu?

    public class Loader {
        {
            System.out.println("a");
        }

        X x;
        static {
            System.out.println("b");
        }

        Loader() {
            System.out.println("c");
        }

        {
            System.out.println("d");
        }

        static class X {

            static {
                System.out.println("e");
            }
        }

        public static void main(String[] args) {
            System.out.println("f");
            new Loader();
        }
    }

Przykład drugi

Napotkawszy poniższy kawałek kodu stwierdzamy (odkrywczo!), że się nie kompiluje, ale rzecz jest prosta i jego poprawienie zajmuje chwilę. Niestety recenzent naszej poprawki stwierdza, że występują następujące ograniczenia w modyfikacji kodu:

Jak poprawić następujący kod, aby się skompilował, a wynik jego działania nie uległ zmianie?

    class Parent {
    }

    class A extends Parent {
        public boolean test() {
            return false;
        }
    }

    class B extends Parent {
        public boolean test() {
            return true;
        }
    }

    public class X {
        public static void main(String[] args) {
            A a = new A();
            B b = new B();
            boolean t = (true ? a : b).test();
        }
    }

Odpowiedzi:

Przykład pierwszy:

Wynik będzie następujący:

b – po załadowaniu klasy wykonane zostaną bloki statyczne,

f – uruchomienie metody main() spowoduje wykonanie umieszczonych w niej instrukcji,

a, d – stworzenie obiektu klasy Loader spowoduje wykonanie kolejnych (wg ich pozycji w klasie) bloków inicjalizacyjnych klasy,

c – wywołanie właściwego konstruktora klasy.

Litera e nie zostanie wydrukowana, gdyż klasa wewnętrzna statyczna jest ładowana w momencie jej pierwszego wykorzystania. W powyższym przykładzie nie jest taka klasa tworzona, zatem nie zostanie także załadowana. Fakt, że jest ona atrybutem klasy Loader niczego nie zmienia, gdyż jest to deklaracja typu, sam obiekt nie jest jednak tworzony.

Przykład drugi:

Operator warunkowy nie można użyć z zaproponowaną składnią, gdyż kompilator:

Kompilator na etapie kompilacji nie potrafi określić, jaki będzie wynik działania operatora warunkowego (mimo, że w tym przypadku optymalizacja jest w stanie określić zwracany typ), stąd musi być pewnym, że w obu przypadkach wywołanie metody test() będzie możliwe.

Mimo, że w tym przypadku, oba typy implementują metodę test(), to kompilator, jak już wspomniano, poszukiwania rozpocznie od pierwszej wspólnej klasy nadrzędnej, a zatem Parent, który takiej metody nie implementuje.

Co zatem można zrobić w powyższym przypadku? Skoro operator warunkowy nie może ulec zmianie, to znaczy, że należy zapewnić kompilator, że klasa Parent posiada metodę test(), można to zrobić w następujący sposób:

Najlepszym rozwiązaniem byłoby oczywiście usunięcie operatora warunkowego i zastąpienie go blokiem if-else, w takim przypadku modyfikacja klasy Parent byłaby zbędna.

Źródło: http://www.javaexpress.pl/article/show/Express_killers_part_IV