Nemám rád takové ty testové otázky, které se skládají z výpisu kódu přibližně o dvaceti řádcích následovaného otázkou, co program po spuštění vypíše na konzoli, s možnostmi odpovědí, z nichž ty první v pořadí jsou kompilační chyba, chyba za běhu a nic se nestane. O respondentovi se dozvíte nejspíše jenom to, jak si dokáže všímat detailů. Což se může hodit, ale není to všechno. Navíc například mě osobně takové testy urážejí, protože si s nimi někdo nedal žádnou práci, prostě je opsal z příkladů k certifikaci, a opravování si chce také maximálně ulehčit.
Já mám raději příklady, ze kterých jsem schopen odhadnout, kolik toho má ten člověk za sebou. Jeden z mých oblíbených spočívá v tom, že chci implementovat následující metodu:
/** * Removes from collection all items that contain given text. * Supposes parameters are not null and the collection doesn't contain null. */ public void filter(Collection<String> collection, String text) { // TODO: implement }
Když se ti, kteří neumějí procházet kolekce, odebéřou zjišťovat, jak udělají z kolekce pole, které si pak proběhnou přes indexy, dostaneme první skupinu, která napíše toto:
for (String item : collection) { if (item.contains(text)) { collection.remove(item); } }
Což skončí výjimkou ConcurrentModificationException. Autor kódu tedy něco o Javě ví, ale moc toho nenaprogramoval. Zkušenější, ale stále ještě junior, si zkusí poradit nějak takto:
Collection<String> toBeRemoved = new ArrayList<>(); for (String item : collection) { if (item.contains(text)) { toBeRemoved .add(item); } } collection.removeAll(toBeRemoved);
Což už výjimky generovat nebude. Autor má něco za sebou, ale spokojí se s prvním řešením, které ho napadlo nebo ho někde opsal. Kód, který chci vidět, vypadá takto:
for (Iterator<String> iterator = collection.iterator(); iterator.hasNext();) { String item = iterator.next(); if (item.contains(text)) { iterator.remove(); } }
A je to. Iterátor umí použít kdekdo. Ale dost lidí zná dvě metody toho rozhraní. Tu třetí ignoruje.
Edit: Obdržel jsem rozumnou připomínku, že mé nejlepší řešení nemusí fungovat pro všechny typy kolekcí, ano, z metody může vyletět UnsupportedOperationException. Ale myslím si, že se dá považovat za zodpovědnost volajícího, že chce modifikovat kolekci, která modifikaci umožní.
Kolego, pakliže jste si nadefinoval zadání „Removes from collection all items that don’t contain given text.“, neměla by podmínka ve Vašich příkladech být „if (!item.contains(text))“ ?
Správná připomínka. Upravil jsem zadání a jdu mlátit hlavou do zdi. Díky.
Měl bych takovou připomínku k designu/konvenci. Vím že tady bude mít přednost zadání (nebo Javadoc), ale čekal bych, že metoda filter vrátí novou vyfiltrovanou kolekci. Protože kdybych filtroval z více míst, tak nevím (a neuhlídím) co se mi s tou kolekcí děje. Navíc nemůžu filtrovat dvakrát po sobě (resp. jednotlivé filtry budou v konjunkci).
To je asi relevantní připomínka. Mně se na tomhle líbí, že je to pro volajícího jasně čitelné. Ta metoda nic nevrací, tudíž evidentně modifikuje vstup. Když vidím metodu, která kolekci přebírá a vrací, přiznám se, že pak jdu do zdrojového kódu sondovat, zda se mi v té kolekci hrabe nebo ne. A jsem v pokušení předávat jí unmodifiable proxynu namísto mé kolekce. Jestliže pak jako uživatel void verze metody chci zabránit modifikacím, předám kopii. Tím mám mimo jiné třeby i zajištěno, že se mi nebudou vytvářet a zahazovat kopie kolekcí obřích rozměrů, když to nepotřebuji.