Wybrane elementy języka Java – ciąg dalszy

1 Wybrane elementy języka Java – ciąg dalszyPaweł Zdziars...
Author: Celestyna Stypka
0 downloads 0 Views

1 Wybrane elementy języka Java – ciąg dalszyPaweł Zdziarski

2 Wyjątki Reflection Tworzenie i zarządzanie obiektamiGarbage Collector i finalize() Nowe elementy Javy 1.5 Typy sparametryzowane Covariant return types „autoboxing” Pętla w stylu foreach Bezpieczne (type-safe) enumeracje Statyczne import Metody ze zmienną liczbą parametrów

3 Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Wyjątki Reflection Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Nowe elementy Javy 1.5 Typy sparametryzowane Covariant return types „autoboxing” Pętla w stylu foreach Bezpieczne (type-safe) enumeracje Statyczne import Metody ze zmienną liczbą parametrów

4 Typy sparametryzowaneObecne w np. C++ Używane najczęściej w kontekście różnych operacji na kolekcjach W Javie 1.4 (i wcześniejszych) musimy używać rzutowania List myIntList = new LinkedList(); myIntList.add(new Integer(0)); Integer x = (Integer) myIntList.iterator().next(); aby zapewnić poprawność typów w czasie wykonania. Kompilator wie jedynie, że obiekt typu LinkedList przechowuje elementy typu Object Użycie generics w Javie 1.5 List myIntList = new LinkedList(); Integer x = myIntList.iterator().next();

5 Typy sparametryzowane - składniaDeklaracja: używamy formalnego parametru typu, np. public interface List { void add(E x); Iterator iterator(); } Wywołanie: typ sparametryzowany, np. List myList; „intuicyjne” rozumienie wywołania: każde wystąpienie formalnego parametru typu w deklaracji typu sparametryzowanego zastępujemy faktycznym parametrem typu, np. zastępujemy we wszystkich metodach List wystąpnienia E typem Integer W rzeczywistości, skompilowany kod typu sparametryzowanego istnieje tylko w jednej kopii (jeden plik klasy .class)

6 Typy sparametryzowane - wildcardsJak napisać metodę wypisującą wszystkie elementy kolekcji? void printCollection(Collection c) { for (Object e : c) { System.out.println(e);}} (używamy dodatkowo nowej postaci pętli for) Skoro Collection nie jest nadtypem innej sparametryzowanej kolekcji, musielibyśmy stworzyć odpowiednie metody do wypisywania elementów Collection etc. Collection jest nadtypem wszystkich kolekcji. ? Jest typem wildcard void printCollection(Collection c) { for (Object e : c) { System.out.println(e);}}

7 Typy sparametryzowane – bounded wildcardsNawet jeśli Shape jest nadtypem Circle, Rectangle etc., List nie jest nadtypem List, List Możemy jawnie zadeklarować tzw. upper bound parametru typu List

8 Typy sparametryzowane – implementacja, erasureProblem używania kodu generics z kodem wcześniejszych wersji Javy public String loophole(Integer x) { List ys = new LinkedList; List xs = ys; xs.add(x); ys.iterator().next(); } Jak we wcześniejszym przykładzie, tworzymy alias xs do zmiennej ys typu spararametryzowanego List Tutaj kod się kompiluje (kompilator daje jedynie ostrzeżenie), natomiast użycie błędnego typu daje błąd wykonania (ClassCastException) return (String) ys.iterator().next(); Implementacja generics działa jako przód (front-end) normalnego kompilatora Javy, kod z użyciem generics jest zamieniany na normalny bytecode Javy w trakcie procesu nazwanego erasure

9 Typy sparametryzowane – implementacja, erasureKod sparametryzowany public String loophole(Integer x) { List ys = new LinkedList; List xs = ys; xs.add(x); ys.iterator().next(); } zostanie przetłumaczony do public String loophole(Integer x) { List ys = new LinkedList; return (String) ys.iterator().next(); // run time error Podwójna kontrola typów: translator sprawdza poprawność użycia typów sparametryzowanych (np. nie moglibyśmy zadeklarować List xs) Do wynikowego kodu „zwykłej” Javy dodawane są rzutowania wszędzie, gdzie może wystąpić niepoprawne (pod wzgl. typów) przypisanie

10 Typy sparametryzowane - erasureMechanizm polega na mapowaniu typów sparametryzowanych do typów niesparametryzowanych Erasure klasy niesparametryzowanej nie zmienia definicji tej klasy Typy sparametryzowane tracą typ parametru Tree  Tree Tree  Tree Typ parametru jest mapowany do swojego upper bound T (w klasie Tree)  Object ale Bush T (w klasie Bush)  Color Wstawiane są odpowiednie rzutowania

11 Typy sparametryzowane - erasureCzy skopiluje się class ShoppingCart{ // ... } class ShoppingCart{ ? Nie: po erasure, obie klasy miałyby tą samą nazwę

12 Typy sparametryzowane - erasureCzy skompiluje się class TwoForOneSpecial { public void add(T newRentable) { //... } public void add(W newRentable) { }? Nie: po erasure, obie metody mają te same sygnatury

13 Typy sparametryzowane - erasureCzy skompiluje się class GetAFreeVideoTape { public void add(T anything) { //... } public void add(W videotape) { ? Tak: po erasure dostaniemy kod odpowiadający class GetAFreeVideoTape { public void add(Rentable anything) { public void add(Videotape videotape) {

14 Typy sparametryzowane – jeden kod klasyDlaczego nie jest poprawna konstrukcja class MyClass { static T member; } ? Tworząc wiele instancji typu sparametryzowanego, np. MyClass myIntClass; MyClass myStringClass; mielibyśmy tylko jedną kopię member w pamięci. Jakiego typu byłaby ta zmienna?

15 Typy sparametryzowane – jeden kod klasyJaki wynik da wykonanie List l1 = new ArrayList(); List l2 = new ArrayList(); System.out.println(l1.getClass() == l2.getClass()); ? Ponieważ kod z użyciem generics i tak jest tłumaczony do kodu bez typów sparametryzowanych, wszystkie instancje klas sparametryzowanych są tej samej klasy true

16 Typy sparametryzowane i dziedziczenieFragment kodu List ls = new ArrayList(); List lo = ls; daje błąd kompilacji w linii 2 Intuicyjne rozumienie List jako nadtypu List nie jest poprawne! Rozważając lo.add(new Object()); String s = ls.get(0); Wcześniej stworzyliśmy alias do obiektu lo o nazwie ls W pierwszej linii wstawiamy do listy element typu Object – nie ma już gwarancji, że lista przechowuje elementy wyłącznie typu String Jeśli cały kod kompilowałby się, druga linia nie zachowywałaby poprawności typów Ogólnie, mimo iż A extends B to jednak List nie jest podtypem List

17 Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Wyjątki Reflection Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Nowe elementy Javy 1.5 Typy sparametryzowane Covariant return types „autoboxing” Pętla w stylu foreach Bezpieczne (type-safe) enumeracje Statyczne import Metody ze zmienną liczbą parametrów

18 Covariant return typesDo wersji 1.4 języka przesłaniająca implementowaną w nadklasie metoda podklasy musiała mieć identyczną sygnaturę – w szczególności, zwracany typ Poniższy kod nie kompiluje się w JRE 1.4.1_02 class Fruit implements Cloneable { Fruit copy() throws CloneNotSupportedException { return (Fruit) clone(); }} class Apple extends Fruit implements Cloneable { Apple copy() throws CloneNotSupportedException { return (Apple) clone(); Wywołując clone() na obiekcie Apple dostajemy obiekt nadklasy Fruit i musimy niepotrzebnie rzutować w dół do Apple Java 1.5 dopuszcza taką konstrukcję

19 Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Wyjątki Reflection Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Nowe elementy Javy 1.5 Typy sparametryzowane Covariant return types „autoboxing” Pętla w stylu foreach Bezpieczne (type-safe) enumeracje Statyczne import Metody ze zmienną liczbą parametrów

20 Iterowanie po elementach kolekcjiDotychczas (Java 1.4) używamy konstrukcji typu public void drawAll (Collection c) { Iterator itr = c.iterator(); while (itr.hasNext()) { ((Shape)itr.next()).draw(); } Używając typów parametrycznych, możemy zaoszczędzić sobie kodowania kilku rzutowań public void drawAll (Collection c) { Iterator itr = c.iterator(); while (itr.hasNext()) { itr.next().draw(); }

21 Pętla „foreach” + genericsNowa dopuszczalna postać pętli „for” public void drawAll(Collection c) { for (Shape s:c) s.draw(); } Rozwijane automatycznie do kodu for (Iterator $i = c.iterator(); $i.hasNext();) { Shape s = $i.next();

22 Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Wyjątki Reflection Tworzenie i zarządzanie obiektami Garbage Collector i finalize() Nowe elementy Javy 1.5 Typy sparametryzowane Covariant return types „autoboxing” Pętla w stylu foreach Bezpieczne (type-safe) enumeracje Statyczne import Metody ze zmienną liczbą parametrów

23 Bezpieczne (type-safe) typy wyliczenioweTyp wyliczeniowy – obecny w C, C++, C#, Pascalu Dotychczas public class Karty { public static final int PIK = 0; public static final int TREFL = 1; public static final int KARO = 2; public static final int KIER = 3; } Użycie wzorca (pattern) zamiast konstrukcji języka Potencjalne problemy Metoda oczekująca Karty skompiluje się nawet, jeśli jako parametr przekażemy literał np. 5 Optymalizacja przez kompilator – inlining

24 Typy wyliczeniowe - inliningKompilator optymalizuje kod binarny włączając wartości stałych bezpośrednio do każdej klasy, która ich używa Zgodnie ze specyfikacją języka, narzędzia mogą, ale nie muszą wspierać model rozproszonej pracy nad aplikacją i automatycznie rekompilować klasy używane przez program public class Test { public Test() } static public void main(String[] args) System.out.println(Karty.KARO); Po zrekompilowaniu klasy Karty i zmianie wartości stałej KARO uruchomienie klasy Test (poza IDE) da rezutat identyczny jak przed zmianą stałej

25 Typy wyliczeniowe – wzorzec typesafe enumZdefiniuj klasę reprezentującą pojedynczy element „typu wyliczeniowego” (UWAGA: system typów Javy 1.4 nie obejmuje typu wyliczeniowego) Nie udostępniaj żadnych publicznych konstruktorów Jedynie pola public static final Nigdy nie stworzymy obiektów tego typu poza tymi udostępnianymi przez pola public static final public class KartyPattern { public final String nazwa; private KartyPattern(String nazwa) {this.nazwa = nazwa;} public String toString() {return nazwa;} public static final KartyPattern PIK = new KartyPattern("pik"); public static final KartyPattern TREFL = new KartyPattern("trefl"); public static final KartyPattern KARO = new KartyPattern("karo"); public static final KartyPattern KIER = new KartyPattern("kier"); }

26 Typy wyliczeniowe – wzorzec typesafe enumUżycie wzorca typesafe enum gwarantuje poprawność wykonania (runtime) Jeśli zdefiniujemy metodę oczekującą parametru typu KartyPattern mamy pewność, że każda niezerowa (non-null) referencja przekazana do tej metody reprezentuje poprawny kolor karty Stałe mogą być dodane do takiej klasy bez rekompilowania klas-klientów Stałe nigdy nie są wkompilowane do klas ich używających (klientów) Możemy zmienić metodę toString() aby uzyskać sensowną reprezentację stałych na ekranie Porównywanie stałych odbywa się przez dziedziczoną z Object metodę equals wykonującą porównanie referencji, a nie np. kosztowne porównywanie String’ów Wady: Klasy J2SE i API’s produktów opartych na Javie rzadko używają wzorca typesafe enum, jest on generalnie mało popularny Kod znacznie się rozrasta, jeśli chcemy dodać serializację klasy, uporządkowanie wartości etc.

27 Wzorzec typesafe enum – problemy z serializacjąDodajemy public class KartyPattern implements Serializable Poniższy kod ByteArrayOutputStream bout = new ByteArrayOutputStream (); ObjectOutputStream out = new ObjectOutputStream (bout); KartyPattern kp = KartyPattern.KARO; out.writeObject (kp); out.flush (); ByteArrayInputStream bin = new ByteArrayInputStream (bout.toByteArray ()); ObjectInputStream in = new ObjectInputStream (bin); KartyPattern kp2 = (KartyPattern) in.readObject (); System.out.println ((kp2 == KartyPattern.KARO || kp2 == KartyPattern.KIER || kp2 == KartyPattern.PIK || kp2 == KartyPattern.TREFL)); Wypisuje False W klasie implementującej Serializable musielibyśmy przesłonić metodę readResolve(), żeby upewnić się, że podczas deserializacji nie zostanie stworzona nowa instancja klasy, a użyte będzie stworzone wcześniej statyczne pole..

28 Typy wyliczeniowy w Java 1.5Wbudowany w specyfikację języka nowy typ: wyliczeniowy (enum) enum Karta (pik, trefl, karo, kier); W odróżnieniu od klas wzorca typesafe enum, zmienne takiego typu mogą być używane w klauzuli switch Karta karta; switch karta{ case Karta.pik ... } Nie są konieczne zmiany JVM Implementowane przez kompilator Nowa klasa java.lang.Enum, po której dziedziczą wszystkie zmienne typu wyliczeniowego Deklaracja jest nowym rodzajem deklaracji klasy W szczególności, nie jest legalne jawne tworzenie instancji enumeracji przy użyciu new()

29 Typy wyliczeniowy w Java 1.5A Typesafe Enum Facility for the Javatm Programming Language Effective Java Programming: Substitutes for Missing C Constructs Typesafe Enum: Using enum in J2SE 1.5 (Tiger) Beware of Java typesafe enumerations

30 Literatura i URLs Wprowadzenie do użycia Generics The Java Language Specification, dostępna z java.sun.com