Posts tagged ‘inner classes’

Prawie jak lambda, w Javie

2011-06-02 20:52

Wiadomo powszechnie, że Java listenerami stoi i typowe jest używanie jest różnego rodzaju klas wewnętrznych, które są następnie podawane jako interfejsy do wywołań zwrotnych. W ten sposób obsługuje się różnego rodzaju zdarzenia, począwszy od interakcji użytkownika z programem aż po ważne notyfikacje pochodzące z systemu operacyjnego.
Gdy klasa zawierające takie handlery dostatecznie się rozrośnie, pojawia się oczywiście potrzeba jej zrefaktorowania i podzielenia na dwie lub większą liczbę mniejszych. W trakcie tego procesu czasami chciałoby się też wydzielić owe klasy wewnętrzne, obsługujące zdarzenia – i wtedy możemy napotkać pewien kłopot. Kłopocik właściwie ;)

Dotyczy on zależności między klasami wewnętrznymi a klasą je otaczającą. Ponieważ mówimy o niestatycznych niestatycznych klasach wewnętrznych, typowe jest odwoływanie się do składników klasy otaczającej z klasy zewnętrznej. Mówiąc bardziej po ludzku, implementacja np. zdarzenia kliknięcia przycisku może sięgać do obiektu okna/dialogu/itp., zawierającego tenże przycisk:

  1. private final OnClickListener buttonsListener = new View.OnClickListener() {
  2.     @Override
  3.     public void onClick(View v) {
  4.         if (v.getId() == R.id.exit_button) finish();
  5.     }
  6. }

Przeniesienie całej powyższej klasy w inne miejsce nastręcza wówczas problem, gdyż odwołuje się ona do metody klasy ją otaczającej (czyli finish()). Należałoby więc posiadać obiekt tej klasy, zapewnić aby rzeczona metoda była dostępna (co nie zawsze jest wskazane), aby wywoływać ją z właściwymi parametrami – które też trzeba jakoś przekazać – i tak dalej… Krótko mówiąc na naszej drodze do ładnie zorganizowanego kodu naraz pojawia się sporo przeszkód.

Czy nie dałoby się po prostu jakoś przekazać tego “kawałka kodu”, tych kilku czy kilkunastu instrukcji opakowanych w coś, co można podać w inne miejsce programu?… Okazuje się, że jak najbardziej, a rozwiązaniem na problem refaktorowanych klas wewnętrznych jest… więcej klas wewnętrznych ;-) Nic nie stoi bowiem na przeszkodzie, aby rzeczony fragment procedury zdarzeniowej zapakować w metodę klasy implementującej interfejs Runnable. Tak, dokładnie ten interfejs który zazwyczaj kojarzy się z wątkami i klasą Thread. W rzeczywistości reprezentuje on “cokolwiek co da się uruchomić”, więc jak ulał pasuje w charakterze rozwiązania problemu.
Aplikując je do powyższego przykładu, możemy wydzielić powyższy listener (potencjalnie wraz z kilkunastoma podobnymi) i kazać mu jedynie wywoływać metodę run jakiegoś obiektu Runnable. W niej zaś umieszczamy nasz pierwotny kod i przekazujemy do nowo wydzielonej klasy:

  1. Buttons.setExitAction(new Runnable() {
  2.     @Override
  3.     public void run() { finish(); }
  4. }

Znawcy tematu powiedzieliby, że w ten sposób utworzyliśmy domknięcie (closure), bo w inne miejsce programu przekazaliśmy fragment kodu wraz z jego kontekstem; w tym przypadku jest to chociażby referencja this na rzecz której wywoływany jest finish. Fani programowania funkcyjnego stwierdzą z kolei, że ta technika jest kiepską imitacją wyrażeń lambda i najprawdopodobniej też będą mieli rację.
Nie zmienia to jednak faktu, że jeśli programujemy w starej (nie)dobrej Javie, to technika ta może być po prostu użyteczna – niezależnie od tego, jaki paradygmat za nią stoi. Dlatego też chciałem dzisiaj się nią podzielić.

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


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