JE_34 - Odkazujte se na objekty jejich rozhraními

Rada 25 obsahuje doporučení, že byste jako typy paramterů měli používat rozhraní spíše než třídy. Obecněji platí, že byste měli dávat přednost použití rozhraní před třídami při odkazování na objekty. Existuje-li příslušné rozhraní, pak by měly být parametry, návratové hodnoty a atributy deklarovány pomocí typů rozhraní. Jediným okamžikem, kdy se skutečně musíte odkazovat na třídu nějakého objektu, je jeho vytvoření. Aby bylo vše konkrétnější, zvažme případ třídy Vector, která je implementací rozhraní List. Zyvkněte si na zápis podobný tomuto:

//Dobře - jako typ používá rozhraní
List subscribers = new Vector();

a nikoli tomuhle:

// patně - jako typ používá třídu
Vector subscribers = new Vector();

Zvyknete-li si na používání rozhraní jako typů, bude váš program mnohem flexibilnější. Rozhodnete-li se ke změně implementací, stačí změnit jen název třídy v konstruktoru (nebo použít jinou statickou tovární metodu). Například první deklaraci lze změnit tak, že bude mít tvar:

List subscribers = new ArrayList();

a veškerý okolní kód bude nadále fungovat. Okolní kód nevěděl nic o starém implementačním typu, takže změnu nezaznamená. Je tu ale jeden chyták: pokud nabízela původní implementace nějakou speciální funkčnost nevyžadovanou obecným kontraktem rozhraní a kód na této funkčnosti závisel, pak je důležité, aby nová implementace poskytovala stejnou funkčnost. Kdyby byl například kód kolem první deklarace závislý na skutečnosti, že třída Vector je synchronizovaná, pak by bylo nepsrávné nahradit v deklaraci Vector položkou ArrayList.

Proč byste ale měli měnit implementace? Protože nová implementace nabízí leoší výkon nebo protože poskytuje dostatečno funkčnost. Příklad z reálného světa se týká třídy ThreadLocal. Interně tato třída používá přátelský atribut Map ve Thread k přiřazení hodnot jednotlivých vláken instancím ThreadLocal. Ve verzi 1.3 byl tento atribut inicializován na instanci Hashmap. Ve verzi 1.4 byla na platformu přidána nová, specializovaná implementace nazvaná IdentityHashmap. Změnou jediného řádku kódu inicializování daného atributu na IdentityHashMap místo HashMap se stal prvek ThreadLocal rychlejším.

Kdyby byl atribut deklarovaný jako HashMap namísto Map, pak by nebylo zaručeno, že bude změna jediného řádku dostačovat. Pokud by klientský kód používal operace HashMap mimo rozhraní Map nebo předával mapu nějaké metodě vyžadující HashMap, pak by se kód již nezkompiloval, jakmile by došlo ke změně atributu na IdentityHashMap. Deklarování atributu s typem rozhraní "zachovává vaši čest".

Je naprosto v pořádku odkazovat se na objekt prostřednictvím třídy a nikoli rozhraní, pokud žádné vhodné rozhraní neexistuje. Zvažme například hodnotové třídy, jako jsou String a BigInteger. Hodnotové třídy se jen zřídka píší s uvažováním více implementací. Často jsou finální a jen zřídka mají odpovídající rozhraní. Je naprosto v pořádku používat hodnotovou třídu jako typ parametru, proměnné, atributu nebo návratové hodnoty. Obecněji platí, že pokud nemá konkrétní třída žádné přiřazené rozhraní, pak si nemůžete vybrat a musíte se odkazovat prostřednictvím dané třídy, ať už reprezentuje nějakou hodnotu nebo ne. Do této kategorie spadá třída Random.

Druhým případem, kdy neexistuje žádné vhodné rozhraní, je situace s objekty patřícími do nějakého rámce, jehož základní typy jsou třídy a nikoli rozhraní. Pokud nějaký objekt patří do takového rámce založeného na třídách, pak je lepší odkazovat se na něj odpovídající bázovou třídou, která je obvykle abstraktní, a nikoli jeho implementační třídou. Do této kategorie spadá třída java.util.TimerTask.

Posledním případem, kdy neexistuje žádné vhodné rozhraní typu, jsou třídy, ktré implementují nějaké rozhraní, ale poskytují další metody, jež se v takovém rozhraní nenacházejí - příkladem je LinkedList. Takovou třídu je zapotřebí používat k odkazování na její instance, pouze pokud program spoléha na dodatečné metody: nikdy by neměla být použita jako typ parametru (rada 25).

Tyto příklady nejsou vyčerpávající, ale jen nasti�ují situace, kdy je lepší odkazovat se na objekt jeho třídou. V praxi by mělo být zřejmé, zda má daný objekt vhodné rozhraní. Pokud ano, pak bude váš program flexibilnější, použijete-li toto rozhraní k odkazování na objekt; pokud ne, pak jednoduše použijte nejvyšší třídu v hierarchii tříd poskytující požadovanou funkčnost.