Wybrane elementy języka Java – ciąg dalszy

30
Wybrane elementy języka Java – ciąg dalszy Paweł Zdziarski

description

Wybrane elementy języka Java – ciąg dalszy. Paweł Zdziarski. 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 - PowerPoint PPT Presentation

Transcript of Wybrane elementy języka Java – ciąg dalszy

Page 1: Wybrane elementy języka Java – ciąg dalszy

Wybrane elementy języka Java – ciąg dalszy

Paweł Zdziarski

Page 2: Wybrane elementy języka Java – ciąg dalszy

• 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

Page 3: Wybrane elementy języka Java – ciąg dalszy

• 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

Page 4: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane

• Obecne 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ć rzutowaniaList 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.5List<Integer> myIntList = new LinkedList<Integer>(); myIntList.add(new Integer(0));Integer x = myIntList.iterator().next();

Page 5: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane - składnia

• Deklaracja: używamy formalnego parametru typu, np.

public interface List<E> {

void add(E x);

Iterator<E> iterator();

}• Wywołanie: typ sparametryzowany, np.

List <Integer> 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<E> wystąpnienia E typem Integer

• W rzeczywistości, skompilowany kod typu sparametryzowanego istnieje tylko w jednej kopii (jeden plik klasy .class)

Page 6: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane - wildcards

• Jak napisać metodę wypisującą wszystkie elementy kolekcji?void printCollection(Collection<Object> c) { for (Object e :

c) { System.out.println(e);}}

(używamy dodatkowo nowej postaci pętli for)

– Skoro Collection<Object> nie jest nadtypem innej sparametryzowanej kolekcji, musielibyśmy stworzyć odpowiednie metody do wypisywania elementów Collection<String> etc.

• Collection<?> jest nadtypem wszystkich kolekcji. ? Jest typem wildcard

void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e);}}

Page 7: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane – bounded wildcards

• Nawet jeśli Shape jest nadtypem Circle, Rectangle etc., List<Shape> nie jest nadtypem List<Circle>, List<Rectangle>

• Możemy jawnie zadeklarować tzw. upper bound parametru typu

List <? extends Shape>

Page 8: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane – implementacja, erasure

• Problem używania kodu generics z kodem wcześniejszych wersji Javypublic String loophole(Integer x) { List<String> ys = new LinkedList<String>;List xs = ys;xs.add(x); ys.iterator().next();}

– Jak we wcześniejszym przykładzie, tworzymy alias xs do zmiennej ys typu spararametryzowanego List<String>

– 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

Page 9: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane – implementacja, erasure

• Kod sparametryzowanypublic String loophole(Integer x) { List<String> ys = new LinkedList<String>;List xs = ys;xs.add(x); ys.iterator().next();}

zostanie przetłumaczony do

public String loophole(Integer x) { List ys = new LinkedList;List xs = ys;xs.add(x);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<Object> xs)– Do wynikowego kodu „zwykłej” Javy dodawane są rzutowania wszędzie, gdzie może wystąpić niepoprawne (pod wzgl. typów)

przypisanie

Page 10: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane - erasure

• Mechanizm polega na mapowaniu typów sparametryzowanych do typów niesparametryzowanych

• Erasure klasy niesparametryzowanej nie zmienia definicji tej klasy• Typy sparametryzowane tracą typ parametru

Tree<T> TreeTree<Integer> Tree

• Typ parametru jest mapowany do swojego upper boundT (w klasie Tree) Object

aleBush <T extends Color>

T (w klasie Bush) Color• Wstawiane są odpowiednie rzutowania

Page 11: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane - erasure

• Czy skopiluje się class ShoppingCart<T extends DVD>{ // ...}

class ShoppingCart<T extends VideoTape>{ // ...}?• Nie: po erasure, obie klasy miałyby tą samą nazwę

Page 12: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane - erasure

• Czy skompiluje się

class TwoForOneSpecial<T extends Rentable, W extends Rentable> {

public void add(T newRentable) {

//...

}

public void add(W newRentable) {

//...

}

}?

• Nie: po erasure, obie metody mają te same sygnatury

Page 13: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane - erasure

• Czy skompiluje się class GetAFreeVideoTape<T extends Rentable, W extends VideoTape> { public void add(T anything) { //... } public void add(W videotape) { //... }}?• Tak: po erasure dostaniemy kod odpowiadającyclass GetAFreeVideoTape { public void add(Rentable anything) { //... } public void add(Videotape videotape) { //... }}

Page 14: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane – jeden kod klasy

• Dlaczego nie jest poprawna konstrukcja

class MyClass<T> {

static T member;

}

?

•Tworząc wiele instancji typu sparametryzowanego, np.MyClass<Integer> myIntClass;

MyClass<String> myStringClass;

mielibyśmy tylko jedną kopię member w pamięci. Jakiego typu byłaby ta zmienna?

Page 15: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane – jeden kod klasy

• Jaki wynik da wykonanie

List <String> l1 = new ArrayList<String>();

List<Integer> l2 = new ArrayList<Integer>();

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

Page 16: Wybrane elementy języka Java – ciąg dalszy

Typy sparametryzowane i dziedziczenie

• Fragment koduList<String> ls = new ArrayList<String>(); List<Object> lo = ls;

daje błąd kompilacji w linii 2• Intuicyjne rozumienie List<Object> jako nadtypu List<String> nie jest poprawne!

Rozważająclo.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<B> nie jest podtypem List<A>

Page 17: Wybrane elementy języka Java – ciąg dalszy

• 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

Page 18: Wybrane elementy języka Java – ciąg dalszy

Covariant return types

• Do 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_02class 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ę

Page 19: Wybrane elementy języka Java – ciąg dalszy

• 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

Page 20: Wybrane elementy języka Java – ciąg dalszy

Iterowanie po elementach kolekcji

• Dotychczas (Java 1.4) używamy konstrukcji typupublic 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<Shape> c) { Iterator<Shape> itr = c.iterator();

while (itr.hasNext()) {itr.next().draw();

}}

Page 21: Wybrane elementy języka Java – ciąg dalszy

Pętla „foreach” + generics

• Nowa dopuszczalna postać pętli „for”

public void drawAll(Collection<Shape> c) { for (Shape s:c)

s.draw();}

• Rozwijane automatycznie do kodu

for (Iterator<Shape> $i = c.iterator(); $i.hasNext();) {Shape s = $i.next();

s.draw();}

Page 22: Wybrane elementy języka Java – ciąg dalszy

• 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

Page 23: Wybrane elementy języka Java – ciąg dalszy

Bezpieczne (type-safe) typy wyliczeniowe

• Typ 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

Page 24: Wybrane elementy języka Java – ciąg dalszy

Typy wyliczeniowe - inlining• Kompilator 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

Page 25: Wybrane elementy języka Java – ciąg dalszy

Typy wyliczeniowe – wzorzec typesafe enum

• Zdefiniuj 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"); }

Page 26: Wybrane elementy języka Java – ciąg dalszy

Typy wyliczeniowe – wzorzec typesafe enum

• Uż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.

Page 27: Wybrane elementy języka Java – ciąg dalszy

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));• WypisujeFalse• 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..

Page 28: Wybrane elementy języka Java – ciąg dalszy

Typy wyliczeniowy w Java 1.5

• Wbudowany 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 switchKarta 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()

Page 29: Wybrane elementy języka Java – ciąg dalszy

Typy wyliczeniowy w Java 1.5

• A Typesafe Enum Facility for the Javatm Programming Languagehttp://www.jcp.org/aboutJava/communityprocess/jsr/tiger/enum.html• Effective Java Programming: Substitutes for Missing C Constructshttp://java.sun.com/developer/Books/shiftintojava/page1.html#

replaceenums• Typesafe Enum: Using enum in J2SE 1.5 (Tiger)http://www.langrsoft.com/articles/enum.html• Beware of Java typesafe enumerationshttp://www.javaworld.com/javaworld/javatips/jw-javatip122.html

Page 30: Wybrane elementy języka Java – ciąg dalszy

Literatura i URLs

• Wprowadzenie do użycia Generics

http://developer.java.sun.com/developer/technicalArticles/releases/generics/

• The Java Language Specification, dostępna z java.sun.com