Niestatyczne klasy wewnętrzne w praktyce

2010-08-16 20:35

Postawiłbym tezę, że każdy szerzej używany język programowania ma ok. jedną wyróżniającą cechę, definiującą w znacznym stopniu sposób, w jaki się go używa. Innymi słowy, każdy język z czymś się kojarzy. I tak myśląc o C, przypominamy sobie od razu wskaźniki; w Pythonie przychodzi nam na myśl składnia oparta na wcięciach; Pascal kojarzy nam się natychmiast z begin/end; w LISP-ie mamy miliardy nawiasów; w C++ przeciążanie operatorów; w PHP kod przeplatany wstawkami HTML – i tak dalej. Chyba tylko C# jest – przynajmniej dla mnie – swego rodzaju wyjątkiem w tej kwestii (co niekoniecznie musi być złe, bo oznacza również brak wyraźnie irytujących feature‘ów).

Dzisiaj jednak chciałem napisać o Javie z tego względu, że przyszło mi ostatnio kodować nieco w tym języku. Wrażenie, jakie w związku z tym odnoszę, jest takie, iż Java to obecnie chyba najbardziej “klasycznie obiektowy” język ze wszystkich, które mają w branży jakieś znaczenie. Tu nie ma żadnych udziwnień, które łamałyby paradygmat OOP-u, na który składają się m.in. obiekty dostępne przez referencje, komunikacja za pomocą interfejsów i metod, wyraźny podział na proste typy wbudowane i złożone klasy, jawne tworzenie kopii obiektów, i tak dalej. Z jakichś powodów “wynalazki” w rodzaju typów generycznych czy wyrażeń lambda przebijają się do Javy bardzo, bardzo powoli.
Do tej listy trzeba też dopisać jakąkolwiek formę wskaźników na funkcje, przydatną w implementacji callbacków, czy też delegatów przeznaczonych do obsługi zdarzeń. Zamiast tego javowe API muszą uciekać się do (znów wybitnie OOP-owego) implementowania ustalonych interfejsów i polimorficznych wywołań metod. Do tego jednak język posiada unikalny feature, który – przynajmniej w założeniu – ma ten proces wydatnie ułatwiać. To właśnie tytułowe niestatyczne klasy wewnętrzne (inner classes).

Idea jest prosta. Jeśli klasę B umieścimy wewnątrz klasy A, to zabieg ten w Javie będzie miał skutek nie tylko dla widoczności tej pierwszej. Klasa B będzie wtedy klasą wewnętrzną A także w tym sensie, że każdy jej obiekt będzie zawsze związany z jakimś obiektem klasy A. Tą związanie odbywa się automatycznie i objawia dostępem do składników klasy “otaczającej”:

  1. public class Foo {
  2.     class Bar {
  3.         public void Oyey() { Boo{}; }
  4.     }
  5.     private Bar bar = new Bar();
  6.     public void Boo() { }
  7. }

To w sumie nie jest aż tak imponujące. Wprawnym okiem można łatwo zauważyć, że to w gruncie rzeczy tylko cukierek składniowy dla przekazywania this do konstruktora klasy wewnętrznej i późniejszego odwoływania się do niego. Ciekawiej jest wtedy, gdy mała, wewnętrzna, pomocnicza klasa jest na tyle mała i pomocnicza, że nie warto nawet nadawać jej nazwy, bo potrzeba nam tylko jednego jej obiektu:

  1. public class Foo {
  2.     private ISomeInterface si = new ISomeInterface() {
  3.         @Override
  4.         public void someMethod() { }
  5.     };
  6. }

Wówczas wszystko co o tej klasie wiemy to to, że implementuje ona pewien interfejs i właśnie za jego pośrednictwem możemy się do jej jedynego obiektu odwoływać.

Tak w skrócie wygląda teoria. Z praktycznego punktu widzenia mogę natomiast powiedzieć, że dwojga złego lepiej już te wątpliwej świeżości cukierki składniowe mieć, skoro innego wyjścia nie ma… Mam tu na myśli oczywiście fakt, że wobec braku delegatów/zdarzeń/domknięć/itp. jedynym sposobem na komunikację zwrotną w aplikacjach javowych jest wywoływanie metod z ustalonych interfejsów, implementowanych przez obiekty, które następnie podaje się jako “słuchacze” (listeners). Niestatyczne klasy wewnętrzne zapewniają przynajmniej łatwe połączenie między tymi sztucznie wprowadzonymi obiektami i resztą programu.
Trzeba jednak uważać, by nie zamienić swojego kodu w spaghetti, co może się łatwo zdarzyć, jeśli radośnie wpleciemy definicje klas w treść funkcji. Jest to szczególnie niepożądane w kodzie inicjalizującym np. elementy UI, w którym należy po kolei poustawiać wszystkie listenery dla wszystkich kontrolek. Zdefiniujmy je wszystkie w locie i będziemy mieli całą obsługę zdarzeń w jednej metodzie. Fuj!

Dlatego lepiej już definiować takie pomocnicze obiekty w podobny sposób, jak wyżej – tj. jako pola, którym przypisujemy klasy stworzone ad hoc, zawierające metody z reakcjami na zdarzenia. To oczywiście tylko nędzna imitacja składni prawdziwych procedur zdarzeniowych, ale przynajmniej jest to podróbka akceptowalna.

Tags: , , ,
Author: Xion, posted under Programming »


5 comments for post “Niestatyczne klasy wewnętrzne w praktyce”.
  1. Liosan:
    August 19th, 2010 o 0:33

    Anonimowe klasy wewnętrzne definiowane w metodach mają też dostęp do finalnych zmiennych i parametrów tychże metod – co w zasadzie jest realizacją domknięcia – pomijając “szczegół” w postaci klasy, która zwracaną “funkcję” otacza :)

  2. moriturius:
    August 23rd, 2010 o 11:27

    “Klasa B będzie wtedy klasą wewnętrzną A także w tym sensie, że każdy jej obiekt będzie zawsze związany z jakimś obiektem klasy A.”

    Hmm… jeśli dobrze rozumiem to co napisałeś to chyba niekoniecznie jest prawdą ;)

    Jeśli klasa wewnętrzna jest zdefiniowana jako publiczna to można utworzyć obiekt tej klasy w taki sposób: A.B nazwa_obiektu;

  3. Xion:
    August 23rd, 2010 o 13:50

    To utworzy ci tylko zmienną typu A.B. Chcąc teraz stworzyć obiekt tego typu, musisz mieć najpierw obiekt klasy A, np. a. Wtedy robisz:

    1. A.B b = a.new B();

    i nowo powstały obiekt klasy B jest związany z obiektem klasy A. Jeśli tworzysz B wewnątrz metody A, to oczywiście nie musisz pisać this.new B();, bo wystarczy samo new B(); i Java automatycznie skojarzy nowy obiekt z thisem A. Ale jeśli spróbujesz zrobić

    1. A.B b = new B();

    poza klasą A, to wówczas nie będzie żadnego obiektu A do związania z nowym obiektem B, więc w czasie kompilacji będzie błąd.

  4. TeWu:
    August 27th, 2010 o 15:46

    taaak.. zdecydowanie domknięcia to jest to na co wszyscy javamaniacy czekają najbardziej. Jednak jako, że zrezygnowali z wprowadzenia tego featureu w Javie 7 to pewnie poczekamy sobie na to jeszcze conajmniej 3 lata.

  5. NullPointerException:
    August 28th, 2010 o 12:32

    @TeWu, masz nieaktualne informacje ;P

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.