CZJUG – 21. února 2007
description
Transcript of CZJUG – 21. února 2007
1/73
CZJUG – 21. února 2007
Generické typyv Javě
Ing. Tomáš Záluskýhttp://www.zalusky.eu
ET NETERA, a.s.http://www.etnetera.cz
2/73
Agenda
● Úvod● Generické typy (třídy, terminologie)● Generické typy (metody, wildcards)● Aplikace v Javě● Praktické situace při používání generik● Diskuse/dotazy
3/73
Když se řekne generický typ● „přestaňte mi nadávat, nerozumím vám“
– kdo nikdy o g. neslyšel v žádném jazyce● „jo, to jsou ty zobáky, abys nemusel furt
přetypovávat ty prvky Listu“– základní kolekce, začátky
● „to, co je v Javě, je dobrý, ale na svý věci to moc nevyužívám, a ty otazníky fuj to je magie“– aktivní používání cizího kódu, občas vlastní
● „už jsem si někdy udělal třídy, kde jsem využil meze, wildcards nebo triky s inferencí a je to užitečný, i když mě to občas vypeklo“– návrh několika vlastních (spolupracujících) g. tříd
4/73
Úvod
● o přednášce● dotazník, různé názory na generiky● očekávání z přednášky
– teorie/praxe, úroveň pokročilosti– přednáška pro kodéry, ne pro architekty– snaha o výklad formou „uzavřený systém“, i za
cenu nepřesností a zjednodušení● co od přednášky neočekávat
– spekulace nad budoucím vývojem– Bolí vás zuby? Generické třídy Vám pomůžou!
5/73
Motivace
● podobnost algoritmů a datových struktur● příklad: seznam řetězců x seznam čísel class SeznamRetezcu { private String array[]; ... public String get(int index) {return array[index];} public void set(int index, String value) {array[index] = value;} public String toString() ... public String max(Comparator comp) { String temp = array[0]; for (String current : array) { if (comp.compareTo(current,temp) > 0) {temp = current;} } return temp; }}class SeznamCisel { private Integer array[]; ... public Integer get(int index) {return array[index];} public void set(int index, Integer value) {array[index] = value;} public String toString() ... ...}
6/73
Motivace – nevýhody ukázky…
● vnější (mezi různými třídami)– opakující se copy & paste kód– není (jazykově) vyjádřena souvislost mezi třídami
● jak by vypadal předek AbstractSeznam? moc by nepomohl
– obtížné rozlišení typu za běhu● reflection, Class.forName("Seznam" + chciInt? "Cisel" : "Retezcu")
7/73
…Motivace – nevýhody ukázky
● vnitřní (v rámci jedné třídy)– není vyjádřena souvislost mezi členy třídy
● např. shoda typu vkládané a vybírané hodnoty– není vyjádřena souvislost mezi parametry metody
navzájem, nebo mezi parametry a návratovou hodnotou
● např. u max() by mělo být zaručeno, že Comparator porovnává objekty stejného typu, jaké vrací max()
● kód je pouze demonstrativní– znalost nevýhod může být důležitá z hlediska
rozhodování se, zda pro řešení daného problému použít generiky
8/73
Možnosti nápravy…
● náhrada za co nejuniverzálnější typ– tj. Object– Java do příchodu 5.0
● odstraní pouze část nevýhod– opakující se kód– vyjádření souvislosti mezi třídami – zadarmo
(založením předka)– rozlišení typu zůstává za běhu za pomoci instanceof
– nevýhody na lokální úrovni zůstávají– zvýšená potřeba přetypovávání
9/73
…Možnosti nápravy…
● generický/parametrizovaný Seznam: (parametrizovaný typem)
class Seznam<T> { private T array[]; ... public T get(int index) {return array[index];} public void set(int index, T value) {array[index] = value;} public String toString() ... public T max(Comparator<T> comp) { // spravne <? super T>, ale tady T temp = array[0]; // to staci for (T current : array) { if (comp.compareTo(current,temp) > 0) { temp = current; } } return temp; }}Seznam<String> seznamRetezcu = new Seznam<String>();
10/73
…Možnosti nápravy
● odstranění všech výše uvedených nevýhod– opakující se kód– zachování souvislostí mezi třídami– rozlišení typu přechází do compile-time– zachování typových souvislostí mezi členy třídy i v
rámci kontraktu metod
11/73
Historie a souvislosti
● C++ template● JSR-14 (http://jcp.org/en/jsr/detail?id=14)● Java 5.0 „Tiger“
12/73
Generické typy I.
● OOP● Generické třídy● Raw typ, erasure, meze● Vztah mezi parametrickými typy
13/73
Jemné připomenutí OOP…
● rozdíl– statický (=deklarovaný) x dynamický (=runtime) typ– List list = new ArrayList();
● „potomek“ a „předek“– kompatibilita vzhledem k přiřazení – dědičnost x implementace interfacu – není tak důležité– objektová hierarchie– kořen Object, větvení stromu „oběma směry“
class Parent {...}class Child extends Parent {...}Parent parent = new Parent(); // lzeChild child = new Child(); // lzeParent parent = child; // lze, ale nutny cast((Child)parent).childMethod();Child child = parent; // nelze, za parentem muze byt v runtime // jiny potomek
14/73
…Jemné připomenutí OOP
15/73
Úvod do generických typů
● T = typový parametr● typový argument
– konkrétní typ dosazený za typový parametr
● parametrizovaný typ– generický typ po dosazení typového argumentu za typový
parametr
● analogie s voláním metod– deklarace: void foo(int p) {...} // p je parametr– volání: foo(2); // 2 je argument
● různé terminologie– parametrizovaný typ x instance parametrizovaného typu– pozor: neplést s instancí ve smyslu nového objektu vytvořeného
pomocí new!
16/73
Odlišnost koncepcí v C++ a Javě
● C++ přístup– každá instance se přeloží do zvláštní třídy, jakoby se
dosadil argument na úrovni zdrojového souboru (makro)
– vytvoří se tolik „syntetických“ tříd, kolik je instancí generického typu
– typová informace je přístupná za běhu● Java přístup
– při překladu se provede kontrola vztahů mezi objekty z hlediska typové kompatibility (přiřazení, cast)
– poté se typová informace odstraní (není tedy přístupná za běhu) a zůstane jedna třída
– tím je zaručena runtime typová bezpečnost
17/73
Generické třídy
● zápisclass Nazev <TypovyParametr1,...> {
// telo tridy
}
● code convention– doporučení: 1písmenný typový parametr– klíč mapy K, hodnota mapy/kolekce V (T,E), výjimka E
(X), obecný typ T (U,…)● typové parametry v <>
– jsou součástí názvu generické třídy– mohou se používat zhruba kdekoli, kde se očekává
třída, tj. přesně:
18/73
Použití generických tříd – kde ano
● tvorba nové instance:... = new ArrayList<T>()
● použití na levé straně přiřazení:List<T> list = ...– přiřazení pole je sice možné, ale nepřináší výhody:
List<T>[] arrayOfLists = ...● deklarace lokální a instanční proměnné:
private List<T> list = ...● vyhození výjimky generického typu:
public <E extends Exception> void method()throws E {...}
● potomek generického předka:public class MySmartList<T> implements List<T>...
19/73
Použití generických tříd – kde ne…
● generické výjimky– generický typ nesmí být odvozen od Throwable
class MyException<T> extends Exception ...● generický enum:
enum Foo<T> {...}● přímá tvorba objektu nebo pole operátorem new:
new T(); new T[] {...}● instanceof:
if (variable instanceof List<String>) ...● class literal:
Class<List<String>> clazz = List<String>.class; ● přímo jako supertyp:
class Foo<T> extends T;
20/73
…Použití generických tříd – kde ne
● tvorba pole prvků generického typu
new ArrayList<T>[]
● import
import java.util.List<String>;
● statický kontext– proměnné, metody, vnitřní statické třídy, interfacy, enums:
01 public class Foo <T,U> {02 static class FooStaticClass {03 void hoo(T arg) {}04 }05 class FooClass {06 void hoo(T arg) {} P U Z Z L E:07 }08 interface FooInterface { Na kterých řádcích ohlásí09 void hoo(T arg); překladač chybu?10 }11 interface Hoo<T> {12 void hoo(T arg,13 U arg2);14 } }
21/73
…Použití generických tříd – kde ne
● tvorba pole prvků generického typu
new ArrayList<T>[]
● import
import java.util.List<String>;
● statický kontext– proměnné, metody, vnitřní statické třídy, interfacy, enums:
01 public class Foo <T,U> {02 static class FooStaticClass {03 void hoo(T arg) {} // NELZE - static context04 }05 class FooClass {06 void hoo(T arg) {} // OK07 }08 interface FooInterface {09 void hoo(T arg); // NELZE - static context10 }11 interface Hoo<T> {12 void hoo(T arg, // T je zcela nový typ, OK13 U arg2); // U je zakázaný14 } }
22/73
Generické třídy
● typový parametr – pouze referenční typ, tj.:● ano:
– přímo typ, tj. vše, co extends Object:
new ArrayList<String>()– pole:
new ArrayList<String[]>()– generický typ:
new ArrayList<ThreadLocal<String>>()– wildcard typ:
new ArrayList<ThreadLocal<?>>()● ne:
– primitivní typ a void– wildcard:
new ArrayList<?>()
23/73
Generické třídy
● parametrizovaný typ:
List<String> listOfStrings = new ArrayList<String>();
● typová kontrola:– překladač ohlídá správný typ:
listOfStrings.add(2005); // chyba– snížení castů:
String s = listOfStrings.get(1);
24/73
Terminologie
● raw type– raw = základní, surový, čistý– typ po odstranění typových parametrů– raw typ ke generickému typu List<String> je List
● type erasure– erasure = vymazání, očištění– proces odstranění typového parametru z generického
typu– provádí jej překladač po kontrole typové kompatibility
25/73
Důsledky
● za běhu se pracuje pouze s raw typem– Příklad: listOfStrings.getClass().getName()– vrací "java.util.List"– nikoli "java.util.List<String>"
● snaha vyhnout se raw typům v deklaracích– může komplikovat závislost na kódu třetí strany
● type erasure = společný důvod všech „kde ne“– např. statický kontext – společný všem instancím
generické třídy– new T – překladač by nevěděl, co má nechat vytvořit,
protože vidí jen Object● typová informace se nedostane do .class souboru
26/73
Shrnutí
● generiky (téměř) nejsou záležitostí runtime !!!
27/73
Meze generických parametrů (bounds)
● specifikují úžeji třídu nebo rozsah tříd typových argumentů● zápis:
– T extends HorniMez T může být HorniMez nebo její potomek (přetížení klíčového slova extends)
– T extends HorniMez & Interface1 & Interface2 T musí splňovat výše uvedenou podmínku pro všechny typy oddělené znakem &
● zúžení typu parametrů může být– absolutní: class Trida <T extends Number> {...}– vzájemné: class Trida <S, T extends S> {...}
● není-li mez uvedena, chápe se jako Object.● type erasure je náhrada typu mezí
28/73
Vztah mezi instancemi generického typu
● List<Integer>– může obsahovat pouze instance třídy Integer
● List<Number>– může obsahovat Integer, Long, Short,...– homogenita a heterogenita jsou relativní pojmy– homogenní = nemohou v něm být Stringy– heterogenní = mohou v něm být různí potomci Number
● mezi instancemi generického typu neexistuje dědění ani kompatibilita vzhledem k přiřazení
● a to ani tehdy, pokud to platí pro typové argumenty
29/73
Instance generického typu – příklad
● důvod:– pokud by to bylo přípustné, šlo by do objektu listOfNumbers uložit Long přes proměnnou listOfNumbers a pak ji chtít vybrat přes listOfInteger => ClassCastException
● rozdíl:statický (deklarovaný) x dynamický (runtime) typ– stejné jako před generikami
List<Integer> listOfIntegers;List<Number> listOfNumbers;listOfIntegers = new ArrayList<Number>(); // chybalistOfNumbers = new ArrayList<Integer>(); // chybalistOfIntegers = listOfNumbers; // chybalistOfNumbers = listOfIntegers; // chybalistOfIntegers = (List<Integer>)listOfNumbers; // chybalistOfNumbers = (List<Number>)listOfIntegers; // chyba
30/73
Vztah generického typu a raw typu
● přiřazení i cast jsou možné, ale vedou na – ● unchecked warning
– upozornění překladače, že nemá dost informací, aby zajistil typovou bezpečnost
– uživatel se dívá na objekt pohledem, který mu umožňuje s ním neoprávněně pracovat
– typicky: vložit do kolekce „vetřelce“– v Eclipse hláška „Type safety“ – při přechodu z 1.4 nevyhnutelné– z dlouhodobého hlediska nežádoucí
a měly by se eliminovat
31/73
Potlačení unchecked warningu
● anotace @SuppressWarnings("unchecked") ● použití
– pokud překladač hlásí unchecked warning, ale ze situace plyne, že práce s typy je bezpečná
– v případě specifické kombinace lenosti, nechutě opravovat warningy a časové tísně
● anotovat lze třída nebo metoda● potlačí všechny warningy v metodě ()
32/73
Odůvodněný @SuppressWarnings
● 2 ukázky: klonování + obejití statického kontextu
public static class Wrapper<T> implements Cloneable {@SuppressWarnings("unchecked")public Wrapper<T> safeClone() throws CloneNotSupportedException {
return (Wrapper<T>)this.clone();}
}
public static class EmptyIterator <T> implements Iterator<T> {
private static EmptyIterator instance = new EmptyIterator();
private EmptyIterator() {}
@SuppressWarnings("unchecked")public static <U> EmptyIterator<U> getInstance() {
return (EmptyIterator<U>)instance;}
public boolean hasNext() {return false;
}...
}
33/73
Generické typy II.
● Generické metody● Wildcards
34/73
Generické metody
● metody mohou být parametrizované podobně jako třídy
● zápis:– deklarace typových parametrů se zapisuje před
návratovou hodnotu– příklady:public <T> int foo(T object) {...}public <T> T max(Collection<T>) {...}
● parametrizovaná metoda(instance generické metody)– analogie k parametrizované třídě– tj. metoda po dosazení typových argumentů za typové
parametry
35/73
Genericita třídy a metody
● jsou nezávislé rysy● generická třída i generická metoda
– příklad:java.util.Collection<E>public <T> T[] toArray(T[])
– parametry spolu nesouvisejí, případně se zastiňují● negenerická třída, generická metoda
– příklad:java.util.Collectionsstatic <T> List<T> singletonList(T o)většina metod
● ostatní kombinace
36/73
Inference…
● type argument inference = odvození typového argumentu– proces nalezení instance generické metody– na základě typu jednotlivých částí kontraktu metody – odehrává se za překladu
public class GenericMethod { public static <T extends Number> void gm(T par) { System.out.println("Number"); } public static <T extends Integer> void gm(T par) { System.out.println("Integer"); } public static <T extends String> void gm(T par) { System.out.println("String"); } public static void main(String[] args) { GenericMethod.gm(1); // Integer GenericMethod.gm("abc"); // String GenericMethod.<Number>gm(1); // Number Number number = new Integer(1); GenericMethod.gm(number); // Number} }
37/73
…Inference…
● mechanismus inference– opírá se jak o typ předaných parametrů, tak o typ
návratové hodnoty– může se lišit v Eclipse compileru a javacu– může vypéct
38/73
…Inference
● explicitní specifikace typu argumentu– sdělíme sami překladači, jaké argumenty dosadit
● u generických tříd vždy– návrhy do JDK7 na zjednodušeníList<String> list = new ArrayList();
● u generických metod potřeba, když automatický mechanismus neodpovídá záměrům programátora
● důvody použití:– negativní: lenost, nechce se zabývat inferencí– pozitivní: snaha o defenzivnost
(zajištění přeložitelnosti na více překladačích)● u instančních metod nutno použít this
this.<Number>gm(1);
39/73
Wildcards…
● co je wildcard– označení pro určitou přesně vymezenou množinu
parametrizovaných typů– prostředek pro zastřešení nekompatibilních
parametrických typů do hierarchie● příklady
– List<? extends Number>– Comparable<? super Integer>– Collection<?>– Map<String,? extends List<T>>
40/73
…Wildcards…
● zápis– ? – neomezený wildcard
● unbounded● čteme: „cokoli“ nebo „neznámý typ“
– ? extends HorniMez – shora omezený● upper bound● čteme: „cokoli co je HorniMez nebo potomek“
– ? super DolniMez – zdola omezený● lower bound● čteme: „cokoli co je DolniMez nebo předek“
● meze mají syntaxi odlišnou od mezí g. typů– žádné vícenásobné meze (&), ale zase super
● další přetížení klíčového slova extends
41/73
…Wildcards…
● wildcard typy– nejsou konkrétní generické typy jako List<String>, Comparable<Number>,…
– ale je nutno je za nimi vidět– wildcard typ se také nazývá rodina konkrétních instancí
generického typu● zápis List<? extends Exception> …
– ...je potřeba číst jako: „homogenní seznam objektů Exception nebo objektů třídy, která je potomkem Exception“
– ...nelze číst jako „seznam, ve kterém může být cokoli, co je potomkem Exception“
– nesprávnému chápání by odpovídal typ List<Exception>, který je pouze podmnožinou wildcard typu
● homogenita x heterogenita– rozdíl Collection<?> x Collection<Object> – viz diagram
42/73
…Wildcards…
43/73
…Wildcards…
● mezi wildcard typy existuje vztah velmi podobný dědění– můžeme používat pojmy „předek“ a „potomek“– wildcard typ T1 je předkem wildcard typu T2, jestliže odpovídající
množina konkrétních parametrizovaných typů pro T1 je nadmnožinou množiny konkrétních parametrizovaných typů pro T2
– lidsky řečeno: když jsou bubliny na obrázku v sobě– kompatibilita vzhledem k přiřazení
● jak zjistit, zda 2 typy jsou ve vztahu předek-potomekpublic class Inheritance {
public static <T,U extends T> void test() {}public static void main(String[] args) {
Inheritance.<Object,String>test();Inheritance.<Collection<? extends Number>,
Collection<? extends Integer>>test();}
}
44/73
…Wildcards
● wildcards je užitečné znát, protože– poskytují vazbu mezi jednotlivými instancemi
generického typu– současně tvoří hierarchii, analogie s hierarchií objektů
● root je Object x Rawtype<?>● wildcard typy x abstraktní třídy
– hierarchie není strom, ale acyklický graf
45/73
Wildcards – pokročilejší
● capture („otisk“, ne „záchytka“ )– fiktivní konkrétní typ, který představuje wildcard typ– je to syntetický typ, se kterým operuje interně překladač při
kontrole zajištění typové bezpečnosti– objevuje se v chybových hlášeních
● omezení volání metod na objektu wildcard typu– List<?> list = ...list.add(1); // chyba
– důvod: neznalost konkrétního parametrizovaného typu– zakázanost metody je dána výskytem T v argumentech metody– metoda vracející T je povolena, přístup přes Object– u omezených wildcards složitější
● víceúrovňové wildcard
46/73
Aplikacev Javě
● Collections● Class● Ostatní
47/73
Collections…
● java.util.Iterable<E> a potomci● java.util.Collection<E>
– void addAll(Collection<? extends E>)● aby bylo možné přidat i kolekci potomků, taktéž u Map.putAll()
– Iterator<E> iterator()– <T> T[] toArray(T[]) // <T super E>
● převede na pole, jehož runtime typ je určen runtime typem zadaného pole● java.util.Iterator<E>● java.util.Map<K,V>
– java.util.Map.Entry<K,V>– Set<Map.Entry<K,V>> entrySet()– Set<K> keySet()– void putAll(Map<? extends K,? extends V>)– Collection<V> values()
48/73
…Collections…
● java.util.Collections<E> (statické metody)– <T> boolean addAll(Collection<? super T>, T...)
● umožní zadat potomka do kolekce předků, nelze totiž<T> boolean addAll(Collection<T>, ? extends T...)
– <T> int binarySearch(List<? extends Comparable<? super T>>, T)
● umožní vyhledat potomka v seznamu předků v případě, že Comparable je definováno na předkovi
– <E> Collection<E> checkedCollection(Collection<E>, Class<E>)
● kolekce s kontrolou typové bezpečnosti za běhu● brání zavlečení nepořádku v podobě směsi generických a raw typů● myšlenka:
když už se nepodaří přesunout kontrolu z runtime do compile-time, měla by se přesunout alespoň co nejblíže místu vzniku chyby
49/73
…Collections
● java.util.Collections<E>, pokračování– <T> void copy(List<? super T>, List<? extends T>)
– <T> List<T> emptyList()– <T extends Object &Comparable<? super T>>T max(Collection<? extends T>)
● Comparable<? super T> je ze stejného důvodu jako u binarySearch● mez Object je z důvodu binární kompatibility● bez Object by po erasure zbyloComparable max(),což by neodpovídalo původnímuObject max()
– void reverse(List<?>)● implementace používá raw typy● možná implementace použitím pomocné generické metody<T> void reverseHelper(List<T>)
50/73
Class…
● java.lang.Class<T>– kompenzuje absenci runtime informace – pokud runtime
informaci potřebujeme, předáme Class<T>– parametrizovaná typem, který třída představuje, např. třída
řetězce "abc" je Class<String>– Class<? super T> getSuperclass()– T newInstance()– boolean isInstance(Object)
● runtime ekvivalent instanceof
– T cast(Object)● umožňuje přetypovat objekt na třídu, kterou známe v runtime, tak aby v
compile-time zůstala zachována schopnost zaručit typovou bezpečnost
public static <T> void filter(Class<T> clazz, List<?> src, List<T> dest) {for (Object o : src) {
if (clazz.isInstance(o)) dest.add(clazz.cast(o));}
}
51/73
…Class
● java.lang.Class<T> (pokračování)– <U> Class<? extends U> asSubclass(Class<U>)– Class<?> forName(String)
● ?, ze Stringu nevíme nic
● java.lang.reflect.Constructor<T>● java.lang.Object
– Class<? extends Object> getClass()● dokumentace:● „Class<? extends X> where X is the erasure of the static type of the
expression on which getClass is called“
Number n = new Integer(1);Class<? extends Number> cn = (Class<? extends Number>)n.getClass();
52/73
Ostatní
● třídy obalující referenci– java.lang.ThreadLocal<T>– java.lang.ref.Reference<T> a potomci
● java.util.concurrent● java.lang.Enum<E extends Enum<E>>
– může být parametrizován pouze podtypem● ne: class StringEnum extends Enum<String>
– potomek Enum je zároveň parametrem● ne: class DayOfWeek extends Enum<Color>
– pozn.: v protipříkladech ignorujeme jazykové opatření zabraňující přímému dědění z Enum
– potomek dědí metody, které závisejí na E● java.lang.reflect
53/73
Praktické situace
● equals a spol.● kovariance● inference● návrhové vzory
54/73
equals, clone a jiná drůbež…
● equals– jak porovnat Box<String> s Box<Integer> ?– nijak, typová informace není v runtime, je potřeba se
spolehnout na equals pro jednotlivé proměnné (které pro String a Integer vrátí false)
public static class Box<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}@Overridepublic boolean equals(Object obj) {
if (!(obj instanceof Box)) return false;if (obj == this) return true;Box<?> that = (Box<?>)obj; // OKBox that = (Box)obj; // OKBox<T> that = (Box<T>)obj; // žádný přínos, unchecked warningBox<T> that = (Box<?>)obj; // errorreturn this.value.equals(that.value);
}...
55/73
…equals, clone a jiná drůbež…
● clone– chceme vrátit stejný parametrizovaný typ, ale Object.clone() předepisuje návratový typ Object
● zúžíme návratový typ● warning u super.clone() ignorujeme
– o instančních proměnných typu T nevíme nic● Cloneable nám nepomůže, je to jen marker interface
(neovlivňuje API, nýbrž chování)● zbývá tedy „zkusmo“ vyvolat metodu clone, …
– ale narazíme na překážky:● clone je protected, nelze vyvolat přímo -> reflexe● metoda ve třídě nemusí být definována, třída není Cloneable, ale přesto to není důvod k selhání Box.clone() (immutable objekty)
– warning u přetypovávání (T) opět ignorujeme
56/73
…equals, clone a jiná drůbež…
● clonepublic static class Box<T> implements Cloneable {
private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}protected Box<T> clone() throws CloneNotSupportedException {
Box<T> retval = null;try {
retval = (Box<T>)super.clone(); // unchecked warning} catch (CloneNotSupportedException e) {
throw e;}try {
retval.value = (T)value.getClass().getMethod("clone",new Class[0]).invoke(value,new Object[0]);// unchecked warning
} catch (NoSuchMethodException e) {/* objekt nema metodu clone, ale promenna je immutable, * a proto nevadi melka kopie * (ta jiz byla provedena v super.clone()) */
} catch (Exception e) {throw new CloneNotSupportedException(e.getMessage());
}return retval;
}...
57/73
…equals, clone a jiná drůbež…
● compareTo– má smysl pouze pokud typový parametr je Comparable
public static class Box<T> implements Comparable<Box<T>> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
public int compareTo(Box<T> that) {T thisValue = this.value;T thatValue = that.value;// zde se nic delat neda, nebot o T nevime nic nez ze je Objectthrow new Error();
}}
public static class CBox<T extends Comparable<T>> implements Comparable<Box<T>> {
.../**public int compareTo(Box<T> that) {
return this.value.compareTo(that.value);//tady uz je to lepsi, vymezili jsme T jako Comparable
}}
58/73
…equals, clone a jiná drůbež…
● Comparable<T>– nelze Super implements Comparable<Super> a současně Sub implements Comparable<Sub> (důvod: erasure)
– možnosti:
● pouze Comparable<Super>– buď: Sub nepřekrývá compareTo(Super), potomci se
porovnávají jen jako Super– anebo: Sub překrývá compareTo(Super), pak se ale musí
povolit porovnání jen pro objekty přetypované na Sub● příklad: Date/Timestamp● nevýhoda: Sub nelze použít jako T extends Comparable<T>● nevýhoda: porušení symetrie:sup.compareTo(sub) projde, alesub.compareTo(sup) skončí výjimkou
59/73
…equals, clone a jiná drůbež
● pouze Comparable<Sub>– nelze použít Super jako Comparable<Super>, což ale
zpravidla moc nevadí (např. Number)– problém se symetrií je odstraněn (zadarmo)
● Sub nedědí Super – kompozice– Joshua Bloch (57 zásad)
60/73
Neexistující kovariance…
● pole– jsou kovariantní
● tj. pole předků je předkem pole potomků– udržují informaci o runtime typu svých prvků
● generické typy– nejsou kovariantní
● tj. dva typy, jejichž typové parametry jsou příbuzné, příbuzné nejsou
– neudržují informaci o runtime typu svých prvků
private static void test1() {Object[] ao = new String[] {"foo"};ao[0] = 1; // ArrayStoreException
}
61/73
…Neexistující kovariance
● kdyby byly generické typy kovariantní, nešlo by:
● v tomto sledu příkazů se někde musel zavést zákaz● na řádku 4? pozdě! nechceme výjimku● na řádku 3? tak se to dělá u polí
– pozn: ekvivalent metody test1() vyjádřený g. typy
● -> na řádku 2 !
1 List<String> ls = new ArrayList<String>();2 List<Object> lo = ls;3 lo.set(1);4 String s = ls.get(1); // ClassCastException
private static void test2() {List<String> ls = new ArrayList<String>();ls.add("foo");List checked = Collections.checkedList(ls,String.class);List<Object> lo = checked; // abychom priklad prelozili,
// pomuzeme si prechodem pres raw typlo.set(0,1);
}
62/73
Pole generických typů…
● iluze– List<String>[] al = new ArrayList<String>[2];
– napravo je zákaz a vlevo to radši ani nezkoušejte● proč je zakázáno vytvořit takové pole?
– pole provádí runtime check– ale v runtime překladač nezná konkrétní typový
argument● na levé straně přiřazení se ale pole uvést smí!?
– ale k ničemu to nevede– protože co by pak mělo být na pravé straně?
● už víme, že ne pole generických typů(přesně: musí tam být pole „reifiable“ typů)
63/73
…Pole generických typů
private static void test3() {// 1. moznost: null - nezajimaveList<String>[] als1 = null;
// 2. moznost: raw typ - ale to umoznuje podvrhnout vetrelceList<String>[] als2 = new List[] {Arrays.asList(0)};als2[0].set(0,"foo"); // !!! ArrayStoreExceptionString s = als2[0].get(0); // !!! ClassCastException
// 3. moznost: negenericky podtyp - ale pak uz je lepsi jej pouzit // i pri staticke deklaraci,class StringList extends ArrayList<String> {}ArrayList<String>[] als3 = new StringList[1];als3[0] = new ArrayList<String>(); // !!! ArrayStoreException, // staticky muzeme, ale v rt to slehneals3[0] = new StringList(); // takze nakonec stejne muzeme jen takto// cili vyuzijeme pouze rozhrani predka, ale to prece potomek podporuje taky!ArrayList<String> element = als3[0];element.size();// pro vyuziti potomka musime pretypovat, takze to vyjde nastejno// jako bychom pouzili potomka rovnou ve staticke deklaraciStringList sl = (StringList)element;
}
64/73
Generiky a design patterns…
● návrhové vzory (DP) jsou založeny na polymorfismu● pružnost DP = za běhu je možné podsouvat jiné objekty● x u generik se snažíme co nejvíce věcí postihnout za
překladu● je to jiný typ polymorfismu, který se na DP ne vždy hodí ● generiky ovlivnily několik návrhových vzorů, ale nelze
čekat velkou revoluci● generické návrhové vzory
– Foo extends Hoo<Foo>● Comparable
– Enum<E extends Enum<E>>● Enum● Node
– C++, více možností, curious recurring template
65/73
…Generiky a design patterns…
● Iterátor– java.util.Iterator<E>– dobrý kandidát na generický typ, T next()
● Bridge– abstrakce se parametrizuje implementací– kód to spíš sváže, implementace nebude možné měnit za chodu– příklad:
● abstrakce: Table (CsvTable, SqlTable)● implementace: View (ViewEverything, PaginatedView)
Table table = new CsvTable(csvFile);View view = new Paginated(8,1);View view2 = new Everything();table.view = view2;table.render(); Table<Paginated> table = new CsvTable<Paginated>(csvFile);
View view = new Paginated(8,1);View view2 = new Everything();//table.view = view2; // Type mismatchtable.view = new Paginated(8,1);table.render();
66/73
…Generiky a design patterns…
● Chain of responsibility– implementace pomocí konstruktoruChainElement(ChainElement successor)
– implementace zřetězením článků● g. typy mohou být využitelné pro vyjádření vazby mezi objekty článku● problém: uspořádaná mapa dvojic Class<T>, Cosi<T>; nelze pomocí java.util.Map, ale implementace je podobná DP chain
● Singleton– nevhodný
● naivní (nefunkční) implementace založená na:public static <T> getInstance()
● tzv. workaround založený na:
– rychle pryč!
public static class SingletonManager {private static Map<Class<?>,Object> table
= new HashMap<Class<?>,Object>();public static synchronized <T> T getInstance(Class<T> clazz) {
T instance = clazz.cast(table.get(clazz)); ...
67/73
…Generiky a design patterns…
● Factory method– paralelní třídní hierarchie– klasická továrna s hierarchií produktů výhody generik moc
nevyužije● svázání factory s produktem – je chtěné?● zúžení typu návratové metody potomka
– výhody se projevují až u metod, které mají typový parametr jako parametr metody
– další využití – statická factory metoda● stále záleží na tom, jak je typově bezpečná prvotní informace, podle které
se rozliší typ vytvářené továrnypublic static abstract class Product {
public abstract void repair();}public static class Java60 extends Product {
public void repair() {System.out.println("uz aby byly Closures ;-)");
}}
68/73
…Generiky a design patterns…public static abstract class Factory {
public abstract Product createProduct();public abstract void repairProduct(Product product);
}
public static class SunMicrosystemsInc extends Factory {public Java60 createProduct() {return new Java60();}@Override // CHYBA, ve skutecnosti neprekryva, ale pretezujepublic void repairProduct(Java60 product) {product.repair();}@Overridepublic void repairProduct(Product product) {
if (!(product instanceof Java60)) throw new IllegalArgumentException();
product.repair(); // nebo ((Java60)product).specifickeRozhrani()}
}//--------------------------------------------------------------------------public static abstract class Factory<T extends Product> {
public abstract T createProduct();public abstract void repairProduct(T product);
}
public static class SunMicrosystemsInc extends Factory<Java60> {public Java60 createProduct() {
return new Java60();}@Override // OKpublic void repairProduct(Java60 product) {
product.repair(); // nebo product.specifickeRozhrani()}
}
69/73
…Generiky a design patterns…
● Visitor● Decorator● AbstractFactory● Template Method
70/73
Co se nevešlo
● inference● generická metoda nebo wildcard?● jak poznat, že má být typ generický● overriding x overloading● bridge method - špeky● reflection
71/73
Odkazy● SUN: http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html● SUN: http://java.sun.com/docs/books/tutorial/java/generics/index.html● SUN Generics forum: http://forum.java.sun.com/forum.jspa?forumID=316● Gilad Bracha: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf● Angelika Langer Generic FAQ:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html● Brian Goetz:
http://www-128.ibm.com/developerworks/edu/j-dw-java-generics-i.html● Brian Goetz generic gotchas:
http://www-128.ibm.com/developerworks/java/library/j-jtp01255.html● kniha: Rudolf Pecinovský: Java 5.0 - Novinky jazyka a upgrade aplikací (Grada)● odkazy na zajímavé problémy, články, blogy:
– typová bezpečnost: http://fpl.cs.depaul.edu/Problem.java– přetížení g. metody:
http://forum.java.sun.com/thread.jspa?forumID=316&threadID=428464– Peter Ahé: http://blogs.sun.com/ahe/entry/inference_and_compound_types– http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html– http://www.artima.com/weblogs/viewpost.jsp?thread=117200
72/73
Q & A
?
73/73
Děkuji za pozornost!
☺