JE_32 - Nepoužívejte řetězce tam, kde jsou vhodnější jiné typy

Úkolem řetězců je reprezentovat text a to dokáží opravdu dobře. Protože řetězce jsou tak obvyklé a tak dobře podporované jazykem, existuje přirozená tendence používat řetězce i k jiným účelům, než pro které byly vytvořeny. tato rada popisuje několik záležitostí, které byste s řetězci neměli dělat.

�etězce jsou slabou náhražkou za jiné hodnotové typy. Když do programu přicházejí nějaká data ze souboru, ze sítě nebo z klávesnice, často se nacházejí v řetězcové formě. Přirozené je ponechat je v této podobě, ale tato tendence je ospravedlnitelná pouze pokud jsou daná data svou podstatou skutečně textová. Jsou-li číselná, měla by být převedena na příslušný numerický typ, jakými jsou int, float nebo BigInteger. Jedná-li se o odpověď na otázku typu ano - ne, pak je zapotřebí data převést na typ boolean. Obecně platí, že existuje-li vhodný typ hodnoty, ať už primitivní nebo jako odkaz na objekt, měli byste jej použít; pokud takový typ neexistuje, měli byste jej napsat. Třeba že se to může zdát samozřejmé, často je tento princip porušován.

�etězce jsou slabou náhražkou za agregační (seskupující) typy. Má-li nějaká entita více komponent, obvykle není vhodné reprezentovat ji jako jediný řetězec. Zde máme například řádek kódu, který pochází z reálného systému - názvy identifikátorů byly změněny, aby kód nikoho konkrétního neobvi�oval:

// Nevhodné použití řetězce jako agregačního typu
String compoundKey = classname +"#"+ i.next();

Tento přístup má mnoho nevýhod. Pokud se znak použitý k oddělování atributů vyksytne v jednom z atributů, výsledkem bude chaos. Chcete-li přistupovt k jednotlivým atributům, musíte řetězec rozdělit, což je pomalé, únavné a často to způsobuje chyby. Nemůžete poskytovat metody equals, toString ani compareTo, ale musíte přijmout chování poskytované typem String. Lepší je prostě napsat třídu představující dné seskupení, často jako soukromou statickou členskou třídu (rada 18).

�etězce jsou slabou náhražkou za schopnosti. Občas se řetězce používají k zajištění přístupu k nějaké funkčnosti. Zvažme například návrh prvku zajišťujícího proměnnou lokální pro nějaké vlákno. Takový prvek poskytuje proměnné, pro které má každé vlákno svou vlastní hodnotu. Při řešení tohoto problému před několika lety přišlo několik lidí nezávisle na sobě se stejným návrhem, v němž klientem osyktované řetězcové klíče zajišťovaly přístup k obsahu proměnné lokální určitému vláknu:

// Chyba - nevhodné používání typu String jako schopnosti!
public class ThreadLocal {
	private ThreadLocal() {
	}

	// Nastavuje hodnotu pojemnované proměnné v aktuálním vlákně
	public static void set(String key, Object value);

	// Vrací hodnotu pojmenované proměnné v aktuálním vlákně
	public static Object get(String key);
}

Problém s tímto přístupem spočívá v tom, že klíče představují sdílený globální obor názvů. Pokud se dva nezávislí klienti daného balíčku rozhodnou používat stejný název pro svou proměnnou lokální vláknu, budou tuto proměnnou neúmyslně sdílet, co obvykle způsobí selhání obou klientů. Také zabezpečení je chabé; zlý klient může schválně použít stejný klíč jako jiný klient a získat tak nepovolený přístup k datům jiného klienta.

Toto API lze opravit náhradou řetězce nezfalšovatelným klíčem (někdy označovaným za schopnost):

public class ThreadLocal {
	private ThreadLocal() {
	}

	public static class Key {
		Key() {
		}
	}

	// Generuje jedinečný, nezfalšovatelný klíč
	public static Key getKey() {
		return new Key();
	}

	public static void set(Key key, Object value);
	public static Object get(Key key);
}

Třebaže toto řeší oba problémy s API používajícího řetězce, může to být ještě lepší. Ty statické metody už vlastně nepotřebujete. Mohou se namísto toho stát metodami instance klíče, v kterémžto okamžiku klíč přestává být klíčem: je to proměnná lokální pro vlákno. V tomto okamžiku už pro vás třída nejvyšší úrovně, jejíž instance nelze vytvářet, již nic nedělá, takže se jí klidně můžete zbavit a přejmenovat vnořenou třídu na ThreadLocal:

public class ThreadLocal {
	public ThreadLocal() {
	}
	public void set(Object value);
	public Object get();
}

Toto je přibližně API poskytované java.util.ThreadLocal. Nejenže řeší problémy řetězcového API, ale je také rychlejší a elegantnější než obě API pracující s klíči.

Souhrnem lze říci, že byste se měli bránit přirozené tendenci reprezentovat objekty jako řetězce, když existují lepší datové typy nebo je lze napsat. Při nechodném použití jsou řetězce těžkopádnější, méně pružné, pomalejší náchylnější k chybám než jiné typy. Mezi typy, místo nichž se řetězce často nesprávně používají, patří primitivní typy, výčtové typy a agregační typy.