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<TimerTask> c) {
            for (Iterator<TimerTask> 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<TimerTask> 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<Integer> myIntList = new LinkedList<Integer>();
        myIntList.add(new Integer(0));
        Integer x = myIntList.iterator().next();
        
    W powyższym przykładzie występuje nowy, dotychczas nieznany w Javie
    zapis: List<Integer>. 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<E> { 
            void add(E x);
            Iterator<E> 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<Integer> i List<String> są instancjami
    tej samej klasy, nie różnymi klasami! <Integer> i <String> 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<String> ls = new ArrayList<String>();  // 1
        List<Object> 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<Object> nową instancję Object... i wszystko się zgadza.
    Tyle, że w następnej linijce z tej samej kolekcji tym razem
    rozumianej jako List<String> próbujemy wyciagnać String... ale
    obiekt, który jest tam obecnie wsadzony to nie jest String! Widać, 
    że założenie, iż List<String> jest podtypem List<Object> 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<Object> c) {
            for (Object e : c) {
                System.out.println(e);
        }}
        
    Pierwsze podejście... i niestety błędne: ograniczyliśmy się do
    wypisywania tylko Collection<Object>, nie dowolnej kolekcji (bo
    Collection<Object> 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<String>;
        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<Ksztalt> znowu da nam
    jednak niepotrzebne ograniczenie, bo przeciez Collection<Kolo>
    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<? extends Ksztalt> 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: <? super T> 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<Object>, 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 <T> void fromArrayToCollection(T[] a, Collection<T> 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<Integer> list = new ArrayList<Integer>();
        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<Integer> list = new ArrayList<Integer>();
        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.