Java 5.0 nowosci: I. Wstep. 29 września 2004 roku Sun wypuścił kolejną wersję Javy, oznaczoną Java 5.0 (wg numeracji wewnętrznej Sun jest to Java 1.5). Ta edycja zawiera wiele nowości i udogodnień, przez wielu (w szczególności przez Sun) określana jest jako najbardziej innowacyjne wydanie Javy od czasu pierwszej edycji z 1998 roku. II. O czym bede mowil. W trakcie referatu skupię się na nowościach wprowadzonych do języka, nie będę omawiał zmian w bibliotekach czy wirtualnej maszynie. III. Nowosci w jezyku: 1. Nowa petla for (tak na rozgrzewkę) Linki: http://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html Do tej pory przechodzenie po kolekcji było paskudniejsze, niz powinno byc. Standardowa metoda przejścia wygląda mniej więcej tak: void cancelAll(Collection c) { for (Iterator i = c.iterator(); i.hasNext(); ) i.next().cancel(); } Ponieważ przejście po wszystkich elementach kolekcji jest operacją, która jest dosyć często wykonywana wprowadzono nowy, ładniejszy mechanizm przechodzenia. Obecnie można do tego wykorzystać nową pętlę for, która na przykładzie wygląda następująco: void cancelAll(Collection c) { for (TimerTask t : c) t.cancel(); } for (TimerTask t : c) oznacza 'dla kazdego elementu t w kolekcji c, a elementy te są to instancje klasy TimerTask wykonaj'. Taka metoda oprócz tego, że krótsza, dzięki czemu łatwiej i zwieźlej oddaje zamiar programisty, nie wymaga też ręcznego określania iteratorów - mniej miejsca na zrobienie błędów. Nowa pętla ma jednak jedną wadę: ukrywa iterator przed programista, co nie zawsze jest pożądane. Takie podejście uniemożliwia np. np. filtrowanie kolekcji, bo do wyrzucenia elementu z kolekcji potrzebny jest dostep do iteratora. W takich przypadkach nalezy używać starego mechanizmu. 2. Typy generyczne Linki: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html http://www.javaworld.com/javaworld/jw-06-2004/jw-0607-tiger2.html Jedną z nowości w Javie 5.0 jest (dla wielu w końcu) wprowadzenie typów generycznych, dzięki którym możliwa staje się praca na typach, których jeszcze nie znamy. Najszersze zastosowanie typów generycznych przewiduje się w różnego rodzaju kolekcjach obiektów. Do tej pory wszystkie kolekcje w Javie trzymały instancje klasy Object, dzięki czemu wprawdzie można było wsadzać do kolekcji wszystkie obiekty jakie chcieliśmy, ale uniemożliwiało sprawdzanie poprawności rzutowań przy wyjmowaniu (no i narzucało w ogóle konieczność rzutowania i pamietania, co w jakiej kolekcji może się znaleźć): List myIntList = new LinkedList(); myIntList.add(new Integer(0)); Integer x = (Integer) myIntList.iterator().next(); W trzeciej linijce tutaj widać rzutowanie na Integery, które tak naprawdę nie powinno być potrzebne - już nazwa listy wskazuje, że programista doskonale spodziewał się jakiego typu obiekty będą przechowywane w liscie - powinien więc móc to wyrazić przy pomocy języka tak wcześnie jak wcześnie tylko się da. Dodatkowo przy ewentualnym błędzie kompilator nie jest w stanie w trakcie kompilacji wychwycić błędów przy rzutowaniu - będą się one objawiały błędami w momencie wykonywania kodu. Te problemy znikają razem z mechanizmem typów generycznych. Teraz można już w momencie deklarowania określić, jaka klasa jest obsługiwana przez daną kolekcję, oraz dzięki temu zapewnić sobie możliwość sprawdzania poprawności klas już w momencie kompilacji. List myIntList = new LinkedList(); myIntList.add(new Integer(0)); Integer x = myIntList.iterator().next(); W powyższym przykładzie występuje nowy, dotychczas nieznany w Javie zapis: List. Jest to własnie moment ukonkretniania klasy List - mówimy, że dana instancja będzie listą przyjmującą i operują- cą tylko i wyłącznie na klasie Integer (nawiasy <> służą w Javie 5.0 własnie do opisu klas generycznych - czy to moment ukonkretniania, czy też w momencie deklarowania klasy działającej na typach generycznych). Dzięki temu później nie musimy już się martwić rzutowaniem spowrotem na Integer przy wyjmowaniu - skoro ta instacja List działa na Integerach wiadomo, co zostanie wyciągniete z kolekcji. Również dzięki temu teraz już w momencie kompilacji można przypilnować, żeby np. przez przypadek nie próbować wyciągnąć z kolekcji Stringa, czy też żeby go nie włożyć. Wiemy już (mniej więcej) jak korzystać z gotowych klas operujących na typach generycznych. Teraz jak sobie samemu taką klasę zdefiniować: public interface List { void add(E x); Iterator iterator(); } Dla osób znających C++ - mechanizm typów generycznych w Javie może się wydawać podobny do mechanizmu template'ów z C++. Jest on jednak zasadniczo różny: w Javie nie tworzą się nowe klasy dla każdego ukonkretnienia typu. List i List są instancjami tej samej klasy, nie różnymi klasami! i są dla niej tylko dodatkowymi parametrami. W tym mechanizmie nie da się metaprogramować - coś podobno możliwego w C++. Teraz kolejna ważna rzecz do zrozumienia na temat generalizacji: załóżmy, że posiadamy klasy A, B - podtyp A, oraz C - kolakcja. Czy C(B) jest podtypem C(A)? Na konkretniejszym przykładzie: List ls = new ArrayList(); // 1 List lo = ls; // 2 załóżmy, że to wszystko ładnie działa. Teraz robimy coś takiego: lo.add(new Object()); // 3 String s = ls.get(0); // 4 W tym momencie już lepiej widać, że coś jest nie tak. W (3) wsadzamy do List nową instancję Object... i wszystko się zgadza. Tyle, że w następnej linijce z tej samej kolekcji tym razem rozumianej jako List próbujemy wyciagnać String... ale obiekt, który jest tam obecnie wsadzony to nie jest String! Widać, że założenie, iż List jest podtypem List prowadzi do poważnych problemów. Dlatego też nie są one spokrewnione w taki sposób, a druga linijka jest w Javie nielegalna. Opisana własność powoduje jednak pewne komplikacje: chcemy po kolei przejść po kolekcji i wypisać wszystkie jej elementy, nie wiemy jednak jakiego typu elementy bedą w kolekcji (konkretniej nie wiemy na jaką klasę kolekcja została ukoknkretyzowana). void printCollection(Collection c) { for (Object e : c) { System.out.println(e); }} Pierwsze podejście... i niestety błędne: ograniczyliśmy się do wypisywania tylko Collection, nie dowolnej kolekcji (bo Collection nie jest nadklasą dla wszystkich innych kolekcji). Czy zastem istnieje jakaś nadklasa dla wszytkich kolekcji? Tak, zapisuje się ją tak: Collection (kolekcja nieznanego). Jest to kolekcja której elementy pasują do każdego możliwego ukonkretnienia. Zatem nasza metoda wypisania wszystkich elementów z kolekcji będzie wyglądać następująco: void printCollection(Collection c) { for (Object e : c) { System.out.println(e); }} Warto tu zwrócić uwagę na fakt, że w pętli for rzutujemy wszystkie obiekty na klasę Object - wprawdzie nie wiemy, co dokładnie będzie zawierała kolekcja, którą bedziemy wypisywać, jednak wiemy na pewno, że same elementy będą podtypem Object. Dlatego można bezpiecznie wykonać taki rzut. Z drugiej jednak strony nie możemy do tej kolekcji dodać obiektu klasy Object Collection c = new ArrayList; c.add(new Object()); gdyż tutaj nie wiemy dokładnie czym jest w naszej kolekcji. Oznacza to, że dodawany przez nas element musiałby być instancją klasy, która jest podtypem tej nieznanej klasy. No a skoro nie znamy tej klasy to nie jesteśmy w stanie tego zapewnić. Collection jest nadtypem każdej kolekcji - co jednak, jeżeli nie potrzebujemy aż tak ogolnego typu? Powiedzmy, że mamy (standardowo) klasę Kształt, oraz jej podtypy Kwadrat, Kolo. Potrzebujemy teraz metodę, która dostanie kolekcję kształtów i następnie je wszystkie narysuje na ekranie. Napisanie Collection znowu da nam jednak niepotrzebne ograniczenie, bo przeciez Collection będzie tak samo dobrze mogło się wypisać. Aby powiedzieć, że dana kolekcja będzie kolekcją dowolnego podtypu Ksztaltu używamy ograniczenia na : void drawAll(Collection c) { ... } W tym momencie metoda może przyjąć zarówno kolekcję kształtów, jak też kolekcję kół i kolekcję kwadratów. Jest to ograniczenie górne na . Istnieją też ograniczenia dolne: co oznacza: dowolny typ będący nadtypem T. Kolejna rzecz: chcemy zdeklarować metodę, która dostanie tablicę obiektów oraz listę obiektów i następnie wsadzi wszystkie obiekty z tablicy do listy. Nie można tu użyć w nagłówku List, to już wiemy. Nie można jednak też użyć List, bo jak wcześniej to pokazałem nie będziemy w stanie nic do takiej listy wsadzić. Rozwiązaniem problemu są metody generyczne: static void fromArrayToCollection(T[] a, Collection c) { for (T o : a) { c.add(o); }} Warto zwrócić uwagę, że przed podaniem typu zwracanego przez metodę podaje się jakie będą typy generyczne w tej metodzie. Ważne jest również to, że przy wywoływaniu takiej metody nie trzeba expicite podawać ukonkretnienia na T - kompilator sam w trakcie kompilacji poszuka najbardziej pasujące (najbardziej dokładne) ukonkretnienie tak aby rzutowania nie generowały błędów. 3. Autoboxing, Autounboxing Kolekcje w poprzednich wersjach Javy nie mogły przyjmować wartości typów pierwotnych, co wywoływało potrzebę opakowywania ich (np int -> Integer) przed włożeniem do kolekcji oraz rozpakowywanie po wyciagnieciu. ArrayList list = new ArrayList(); list.add(0, new Integer(42)); int total = (list.get(0)).intValue(); Od Javy 5.0 to wymaganie zostalo przerzucone na kompilator, dzieki czemu wyzszy kod wyglada tak: ArrayList list = new ArrayList(); list.add(0, 42); int total = list.get(0); 4. Nowy typ Enum Linki: http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html Calkowicie nowy mechanizm stojacy na Enum od Javy 5.0. Do tej pory programisci byli zmuszeni uzywac albo int Enum Patter: public static final int SEASON_WINTER = 0; public static final int SEASON_FALL = 1; ... Albo tez tzw. typesafe enums: public final class Enum { public static final Enum TRUE = new Enum (); public static final Enum FALSE = new Enum (); private Enum () {} } Oba podejscia mialy swoje wlasne, calkiem duze wady, np. static final int: -> brak ochrony typu: ostatecznie sa to zwykle inty, ktore mozna np. dodac do siebie itd. -> brak przestrzeni nazw: trzeba dawac prefiksy, zeby kilka roznych wyliczen mialo tak samo nazwana wartosc. -> wypisane watrosci sa bezuzyteczne: nie wiadomo nawet, co sie wypisalo (int czy nasz enum) typesafe enum: -> nie moga byc uzyte w switch -> zle znosza serializacje (trzeba duzo pracy, aby zadzialaly jak nalezy) Wprowadzono nowy mechanizm: enum Season { WINTER, SPRING, SUMMER, FALL }; Kazdy typ enum ma tez metode values() zwracajaca tablice wartosci w kolejnosci, w jakiej byly zadeklarowane. W polaczeniu z nowym for-em czesto jest to stosowane do przejscia po wszystkich wartosciach danego enumeratora. Dodatkowo, poniewaz nowe enumeratory sa normalna klasa, latwo mozna dodawac do nich specjalne zachowanie, dodatkowe dane, implementowac interfejsy (serializable i comparable juz sa) itd. Np. public enum Planet { MERCURY (3.303e+23, 2.4397e6), VENUS (4.869e+24, 6.0518e6), EARTH (5.976e+24, 6.37814e6), MARS (6.421e+23, 3.3972e6), private final double mass; // in kilograms private final double radius; // in meters Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } public double mass() { return mass; } public double radius() { return radius; } // universal gravitational constant (m3 kg-1 s-2) public static final double G = 6.67300E-11; public double surfaceGravity() { return G * mass / (radius * radius); } public double surfaceWeight(double otherMass) { return otherMass * surfaceGravity(); } } Mozna nawet ukonkretniac metody dla kazdej wartosci osobno: public enum Operation { PLUS { double eval(double x, double y) { return x + y; } }, MINUS { double eval(double x, double y) { return x - y; } }, TIMES { double eval(double x, double y) { return x * y; } }, DIVIDE { double eval(double x, double y) { return x / y; } }; // Do arithmetic op represented by this constant abstract double eval(double x, double y); } Po wiecej ciekawych zastosowan: link 5. Varargs Links: http://java.sun.com/j2se/1.5.0/docs/guide/language/varargs.html Do tej pory aby podac zmienna liczbe parametrow trzeba bylo podawac tablice jako parametr metody. Obecnie mimo, ze dalej na dole przeka- zywana jest tablica, varargs automatyzuje ten proces. Definicja metody o zmiennej liczbie parametrow: public static String format(String pattern, Object... arguments); Trzy kropki oznaczaja mozliwosc wystapienia w tym miejscu tablicy argumentow lub sekwencji argumentow. Varargs moze byc uzyty tylko na ostatnim miejscu argumentow metody. 6. Static import. Links: http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html Do tej pory aby mozna bylo dobrac sie do klasowych pol i metod trzeba bylo zawsze podac nazwe klasy do ktorej zagladamy. double r = Math.cos(Math.PI * theta); Obecnie wprowadzono mechanizm importowania takich pol do naszego kodu. import static java.lang.Math.*; double r = cos(PI * theta); 7. Annotacje Links: http://www.javaworld.com/javaworld/jw-07-2004/jw-0719-tiger3.html http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html Wprowadzenie standardowe: pewne mechanizmy metadanych juz byly obecne w jezyku - tagi do javadoc. Obecnie caly mechanizm rozszerzono, nie tylko dodajac nowe tagi, ale umozliwiajac uzytkownikowi definiowane wlasnych tagow. Wprowadzenie mechanizmu metadanych ma umozliwic bardziej deklaratywny styl pisania: poprzez zdefiniowanie odpowiednich tagow i nastepnie reakcji na nie mozna zrzucic tworzenie duze czesci standardowych czesci kodu (boilerplate code) narzucanych przez API, np. JAX-RPC. Inne API wymagaja jednoczesnego utrzymywania kilku plikow zgodnych ze soba - poprzez metadane mozemy tworzyc i edytowac jeden z nich i poprzez narzedzia generowac drugi (i nastepne). Przyklady annotacji: @Copyright("2002 Yoyodyne Propulsion Systems") public class OscillationOverthruster { ... } @Preliminary public class TimeTravel { ... } Definiowanie wlasnych annotacji: Podobnie do zwyklego interfejsu, trzeba uzyc @interface, np: /** * Indicates that the specification of the annotated API element * is preliminary and subject to change. */ public @interface Preliminary { } Ten znacznik jest pusty (tzw. marker) - nie niesie ze soba zadnej dodatkowej informacji oprocz tego, ze jest). Wiekszy typ: public @interface RequestForEnhancement { int id(); String synopsis(); String engineer() default "[unassigned]"; String date(); default "[unimplemented]"; } Kazda metoda definiuje jedno pole w annotacji. Metody nie moga przyjmowac parametrow, zawierac 'throw', dopuszczale zwracane typy to prymitywy, String, Class, enum, annotacje i tablice wszystkich tu wymienionych. Annotacje w kodzie moga wystapic wszedzie tam, gdzie moga wystapic zwykle 'modifiers' (jak public static itd.), powinno sie dawac jako pierwsze. Tworzenie annotacji: @nazwa { pole1 = wartosc1, pole2 = wartosc2 } wartosci musza byc stalymi w momencie kompilacji. Jezeli jest jedno pole w znaczniku, powinno sie nazywac value() - dzieki temu mozna pominac 'pole =' przy tworzeniu annotacji. Mysle, ze dobrze jest podac prosty przyklad: import java.lang.annotation.*; public @interface Test { } public class Foo { @Test public static void m1() { } public static void m2() { } @Test public static void m3() { throw new RuntimeException("Boom"); } public static void m4() { } } I testowanie: import java.lang.reflect.*; public class RunTests { public static void main(String[] args) throws Exception { int passed = 0, failed = 0; for (Method m : Class.forName(args[0]).getMethods()) { if (m.isAnnotationPresent(Test.class)) { try { m.invoke(null); passed++; } catch (Throwable ex) { System.out.printf("Test %s failed: %s %n", m, ex.getCause()); failed++; } } } System.out.printf("Passed: %d, Failed %d%n", passed, failed); } } Narzedzie do dzialania na annotacjach: atp http://java.sun.com/j2se/1.5.0/docs/guide/apt/index.html ???????????? zdecydowac, ile o tym mowic... ja bym mowil niewiele, bo to temat rzeka ???????????? IV. Nowosci w wirtualnej maszynie: 1. Class data sharing 2. Garbage Collector Ergonomics 3. Thread Priority Changes ?????????? O ILE MOGE LICZYC, ZE BEDZIE JESZCZE CZAS ????????? V. Zmiany w bibliotekach.... duuuzo VI. Podsumowanie Najwieksza przebudowa w jezyku od czasu wejscia na rynek.