31 października 2008

Kolorowanie kodu poprawia czytelność artykułów

Jak pokazują wyniki mojej ostatniej ankiety, zatytułowanej „Czy kolorowanie kodu poprawia czytelność artykułów?”, warto poświęcić chwilkę i zadbać o oprawę graficzną prezentowanych na swoim blogu fragmentów programów. W ankiecie trwającej przez 30 dni oddano 123 głosy. Wyniki ankiety poniżej.


No dobrze. Wiemy więc że warto, pytanie tylko jak to zrobić, żeby się nie narobić? Ja używam Google Code Prettify o którym dowiedziałem się od Jacka Ciereszko. Więcej na ten temat na jego blogu, w artykule „Formatowanie kodu w blogger”.

28 października 2008

SCJP - Statyczne klasy zagnieżdżone

Statyczne klasy zagnieżdżone (ang. static nested classes) to jeden z niewielu tematów, które w książce „SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055)” są mocno niedopowiedziane - w jaskrawym przeciwieństwie do wielu innych, znacznie banalniejszych (choć pewnie ważniejszych) tematów, które są zdecydowanie przegadane, albo wręcz zagadane. Postaram się dziś wypełnić tę lukę, choć mając na uwadze ostateczny cel, tj. przygotowanie do SCJP powstrzymam się od wchodzenia w temat zbyt głęboko. Lubiącym poczytać po angielsku (oraz lubiącym wiedzieć więcej) polecam artykuł „Nested Classes, Part 3”. Przy okazji, przestrzegam czytelników wspomnianej książki o SCJP, że to co napisano we wstępie do rozdziału o klasach zagnieżdżonych jest nieprawdą. Proszę przeczytać więc poniżej zacytowany fragment tejże i zapamiętać, że tak nie jest. Inaczej niż piszą autorzy - klasy zagnieżdżone znajdują się w specjalnym związku z klasami zewnętrznymi (ang. special relationship), o czym za chwilę.

“While an inner class (…) enjoys that special relationship with the outer class (…), a static nested class does not. It is simply a non-inner (also called "top-level") class scoped within another. So with static classes it's really more about name-space resolution than about an implicit relationship between the two classes.”

Czy klasy wewnętrzne (ang. inner classes) i klasy zagnieżdżone (ang. nested classes) to to samo, tylko inaczej nazwane? Jak można się domyślać - nie! Podobnie jak klasę wewnętrzną, klasę zagnieżdżoną definiujemy wewnątrz innej klasy – klasy zewnętrznej – jednak klasy zagnieżdżone, w odróżnieniu od wewnętrznych są to klasy statyczne. Zobaczmy na przykładzie, jak to wygląda.
class Notepad {
private static Date lastCreatedDate;

static class Note {
private Date creationDate;

public Date getCreationDate() {
return creationDate;
}

public Date getLastCreatedDate() {
return lastCreatedDate; // statyczna zmienna prywatna klasy zewnętrznej
}
}

public static Note newNote() {
Note art = new Note();

art.creationDate = new Date(); // zmienna prywatna z klasy wewnętrznej
lastCreatedDate = art.creationDate;

return art;
}
}

Klasa ‘Notepad’ jest w naszym przypadku klasą zewnętrzną a ‘Note’ klasą zagnieżdżoną. Zauważmy, że deklarację klasy ‘Note’ poprzedzono modyfikatorem ‘static’. To właśnie dodając to słówko kluczowe mówimy, że chodzi nam o klasę zagnieżdżoną a nie wewnętrzną. Podobnie jak klasę wewnętrzną, klasę zagnieżdżoną łączy z klasą zewnętrzną pewien szczególny związek, związek oparty na pełnym zaufaniu, a więc taki, który pozwala na dostęp do zmiennych prywatnych swojego partnera. Zaufanie działa w obie strony. Zarówno klasa ‘Note’ ma dostęp do zmiennych prywatnych klasy ‘Notepad’ (zmienna ‘lastCreatedDate’) jak i klasa ‘Notepad’ do zmiennych prywatnych klasy ‘Note’ (zmienna ‘creationDate’). Zwróćmy jednak szczególną uwagę na fakt, że klasy zagnieżdżone to klasy statyczne – a więc nie związane z żadnym obiektem klasy zewnętrznej – stąd mogą one odwoływać się tylko i wyłącznie do zmiennych (i metod) statycznych z tejże klasy zewnętrznej. Jest to sytuacja zupełnie analogiczna do tej w której jesteśmy implementując metodę statyczną, chociażby metodę ‘main(…)’. Możemy wówczas używać jedynie innych statycznych metod bądź statycznych zmiennych. Zmiennych instancyjnych użyć nie możemy, bo przecież nie mamy żadnej instancji!

Zanim przejdziemy dalej zastanówmy się w jakich okolicznościach ta nieco dziwna konstrukcja językowa może się przydać. Szczęśliwie, bardzo dobry przykład jest na wyciągnięcie ręki; zerknijmy na interfejs ‘java.util.Map<K,V>’, a szczególnie na typ wyniku metody ‘entrySet()’. Jest to ‘Set<Map.Entry<K,V>>’. A cóż to takiego ‘Map.Entry<K,V>’? Uwaga, uwaga… statyczny interfejs zagnieżdżony! Tak, zagnieżdżone mogą być także interfejsy i to zarówno w interfejsach jak i w klasach. Generalnie rzecz biorąc, zagnieżdżać można wszystko i we wszystkim, nawet klasy w interfejsach. Interfejs ‘java.util.Map<K,V>’ implementuje klasa ‘java.util.AbstractMap<K,V>’ (która z kolei jest nadklasą klasy ‘java.util.HashMap<K,V>’) a interfejs zagnieżdżony ‘Map.Entry<K,V>’ klasa zagnieżdżona ‘AbstractMap.SimpleEntry<K,V>. Na drzewku dziedziczenia wygląda to tak:

Jak widać z powyższych przykładów, jeśli klasy zagnieżdżonej używamy poza klasą w której została ona zdefiniowana to musimy nazwę tejże poprzedzić nazwą klasy zewnętrznej, zupełnie jakby klasa zewnętrzna była pakietem, w którym zlokalizowano klasę zagnieżdżoną. Pełną nazwą klasy ‘Note’ jest więc ‘Notepad.Note’. Poniżej przykład, pokazujący jak należy identyfikować klasy zagnieżdżone, w zależności od stopnia zagnieżdżenia klasy i umiejscowienia kodu, który tej klasy używa.
class ExternalClass {
void externalTest() {
Notepad.Note.Line line = new Notepad.Note.Line();
}
}

class Notepad {
static class Note {
static class Line {

}

void noteTest() {
Line line = new Line();

// można też tak - za dużo nie ma nigdy
Notepad.Note.Line nextLine = new Notepad.Note.Line();
}
}

void notepadTest() {
Note.Line line = new Note.Line();
}
}

Istnieje też opcja alternatywna do każdorazowego podawania pełnej – poprzedzonej nazwą klasy zewnętrznej – nazwy klasy zagnieżdżonej. Wystarczy klasę zagnieżdżoną zaimportować; tak jak pokazano na poniższym przykładzie.
import my.pckg.Notepad.Note.Line;

class ExternalClass {
void externalTest() {
Line line = new Line();
}
}

class Notepad {
static class Note {
static class Line {

}
}
}

Poza tym co napisano powyżej zagnieżdżone klasy i interfejsy żądzą się tymi samymi regułami co klasy „normalne”, w szczególności, klasy i interfejsy zagnieżdżone mogą być deklarowane jako ‘public’, ‘protected’ czy ‘private’.

17 października 2008

Pierwsze spojrzenie na Blogger Data API

Blogger Data API to część Google Data API (w skrócie GData). Do czego ono służy? Mówiąc najprościej do interakcji (np. odczytywanie i publikowanie artykułów) z blogami hostowanymi przez Bloggera, czyli np. takimi jak mój. API dostępne jest dla wielu (no… dla kilku) języków programowania i zbudowane jest ponad wspólnym protokołem zdefiniowanym przez Google na bazie Atoma, RSSa i czegoś tam jeszcze. No ale nie wnikajmy za nadto w szczegóły – nas interesuje Java a więc fragment dokumentacji zatytułowany „Developer's Guide: Java”.

Naszą przygodę z Blogger Data API zaczynamy od pobrania biblioteki, tj. GData Java Client (plik gdata.java-1.13.0.zip). Pobrany plik rozpakowujemy w dogodnej lokalizacji.

Google przygotowało także wtyczkę (ang. plugin) do Eclipse’a wspierającą programowanie z GData i choć szczerze powiem nie robi ona zupełnie nic ciekawego, to jednak użyję jej, aby pokazać że jest. Więcej informacji o wtyczce na stronie GData Java Client Eclipse Plugin. Niecierpliwi mogą od razu przystąpić do instalacji używając update-site’u http://gdata-java-client-eclipse-plugin.googlecode.com/svn/update-site. Instalujemy więc wtyczkę i tworzymy nowy projekt, jak pokazano poniżej.


W oknie szczegółów wpisujemy nazwę projektu, wybieramy jego typ, tj. ‘Blogger’ oraz wskazujemy lokalizację pobranych uprzednio bibliotek, tj. podkatalogu \java\lib katalogu który powstał po rozpakowaniu pobranego archiwum GData. Zauważmy, że w opisie typu projektu ‘Blogger’ podana jest informacja, że nie ma zewnętrznych zależności („External dependencies required: None”) ale nie jest to prawdą. Na dole okna widzimy opcje sugerujące, że wtyczka jest w stanie samodzielnie pobrać te zależności (JavaMail i JAF) jeśli tylko ją o to poprosimy, ale moje eksperymenty wskazują, że to nie działa (pewnie dlatego, że wtyczka myśli że tych zależności nie ma). Zależności pobierzemy więc później samodzielnie, a póki co klikamy ‘Finish’ i tworzymy projekt.


To co robi zaprezentowana wtyczka to utworzenie zwykłego projektu typu Java, dodanie odpowiednich bibliotek GData do ścieżki budowania (dla projektu typu ‘Blogger’ jest to plik gdata-client-1.0.jar) i wygenerowanie klasy, która łączy się z Blogger’em i pobiera listę blogów zarejestrowanych przez danego użytkownika (identyfikator i hasło podajemy z linii poleceń). Wtyczka dodaje też do ścieżki budowania aplikacji (zbędną) bibliotekę wtyczki (sic)! Struktura naszego projektu jest jak pokazałem poniżej. Na dobry początek polecam eksperymenty z przeklejaniem kodu przykładów ze wspomnianego już rozdziału dokumentacji „Developer's Guide: Java”. Dodam jeszcze, że do odczytu artykułów nie jest wymagane uwierzytelnienie (usuwamy wywołanie funkcji setUserCredentials(…)).

15 października 2008

SCJP - Klasy StringBuffer i StringBuilder

Kilka dni temu – w artykule „SCJP - Klasa String” – pisałem o klasie String. Dziś będzie o dwu kolejnych klasach używanych do operacji na napisach; klasie StringBuffer i StringBuilder.

Klasa String reprezentuje sekwencję znaków, typ napisowy. Jak napisałem w poprzednim artykule, obiekty klasy String są niemodyfikowalne (ang. immutable); każda operacja, w wyniku której chcemy otrzymać inny napis na bazie istniejącego wymaga utworzenia nowego obiektu, do reprezentowania tego nowego napisu. Jest to coś, czego chcielibyśmy uniknąć. Potrzebujemy więc typu, który reprezentował by modyfikowalną sekwencję znaków i właśnie tym jest klasa StringBuffer. Aby zapewnić bezpieczne przetwarzanie wielowątkowe (ang. thread-safe) operacje klasy StringBuffer są synchronizowane. Ułatwia to znakomicie programowanie ale w sytuacji gdy wiemy, że nasza aplikacja nie będzie wykonywana przez więcej niż jeden wątek synchronizacja jest zbędna i wprowadza bezsensowne straty wydajnościowe. Z tego powodu do specyfikacji Java SE w wersji 5 dodano klasę StringBuilder, która różni się od klasy StringBuffer tylko tym, że jej metody synchronizowane już nie są. Poniżej przedstawiam metody tych klas – te ważne z perspektywy egzaminu SCJP – na przykładzie klasy StringBuilder, ale jak wiemy klasa StringBuffer ma metody analogiczne, tyle, że synchronizowane.

public StringBuilder append(String str) - Metoda ta zmodyfikuje łańcuch znaków reprezentowany przez obiekt dla którego ją wywołano w ten sposób, że do jego końca doklei napis przekazany jako argument. Metoda ta ma wiele wariantów różniących się typem parametru; istnieją między innymi wersje dla parametrów typu: int, char, boolean czy Object. Metoda ta zwraca obiekt dla którego została wywołana (nie nowy obiekt jak w klasie String) aby umożliwić wywołania łańcuchowe (ang. chained invocations). Uruchomienie metody
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();

System.out.println(stringBuilder.append("mam ").append(5).append(" lat"));
}
spowoduje wyświetlenie napisu ‘mam 5 lat’.

public StringBuilder delete(int start, int end) - Metoda ta zmodyfikuje obiekt dla którego ją wywołano w ten sposób, że usunie ciąg znaków określony argumentami wywołania. Pierwszy argument to pozycja pierwszego znaku który ma być usunięty (jak zawsze liczymy od 0-ra) a drugi to pozycja pierwszego znaku który usunięty nie będzie. Metoda ta także zwraca obiekt dla którego została uruchomiona. Uruchomienie metody
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("mam 55 lat");

System.out.println(stringBuilder.delete(4, 5));
}
spowoduje wyświetlenie napisu ‘mam 5 lat’.

public StringBuilder insert(int offset, String str) - Metoda ta zmodyfikuje obiekt dla którego ją wywołano w ten sposób, że na pozycji określonej pierwszym argumentem wywołania zostanie wklejony String przekazany jako drugi argument. Analogicznie jak metoda append(..) metoda insert(…) występuje w wielu odmianach, akceptujących jako drugi argument typy takie jak (między innymi): int, float, char, Object. Zwrócony obiekt to obiekt dla którego wywołano metodę. Uruchomienie programu
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("mam 5 lat");

System.out.println(stringBuilder.delete(4, 5).insert(4, "niewiele"));
}
spowoduje wyświetlenie napisu ‘mam niewiele lat’.

public StringBuilder reverse() – Metoda ta odwraca kolejność znaków w obiekcie dla którego ją wywołano. Tak jak dla innych metod, zwrócony obiekt to obiekt dla którego wywołano metodę. Uruchomienie programu
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("abcdef");

System.out.println(stringBuilder.reverse());
}
spowoduje wyświetlenie napisu ‘fedcba’.