JAVA exPress > Archiwum > Numer 5 (2009-10-01) > Express killers, cz. IV

Express killers, cz. IV

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:

  • jest to przykład akademicki i operator warunkowy nie może zostać zmodyfikowany (czyt. metoda main() nie może ulec zmianie),
  • klasy A i B są wykorzystywane także w innych projektach i mimo, że nic nie robią, to nie można zmienić ich API (sygnatury metod oraz hierarchii dziedziczenia).

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:

  • będzie szukał pierwszej wspólnej klasy nadrzędnej dla klas A i B,
  • sprawdzi, czy odnaleziona klasa posiada metodę, która jest wywoływana, a jeśli jej nie znajdzie, zwróci błąd.

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:

  • klasę Parent zamienić na klasą abstrakcyjną i dopisać do niej metodę abstrakcyjną test(), co jednak wiąże się z ograniczeniem takim, że ewentualne wywołanie new Parent() nie będzie poprawne;
  • dodać metodę test() do klasy Parent, która to metodą będzie rzucać jeden z wyjątków mówiących, że nie jest zaimplementowana, co oznacza, że klasa pochodna powinna ją przesłonić.

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.

Nie ma jeszcze komentarzy.

Tylko zalogowani użytkowincy mogą pisać komentarze

Developers World