Programování

Java Tip 99: Automatizujte vytváření toString ()

Vývojáři pracující na velkých projektech obvykle tráví hodiny psaní užitečnými toString metody. I když každá třída nemá vlastní toString metoda, každá třída datových kontejnerů bude. Umožňuje každému vývojáři psát toString jeho vlastní cesta může vést k chaosu; každý vývojář nepochybně přijde s jedinečným formátem. Výsledkem je, že použití výstupu během ladění se stává obtížnějším, než je nutné bez zjevné výhody. Proto by se každý projekt měl standardizovat v jednom formátu pro toString metody a poté jejich tvorbu automatizovat.

Automatizovat toString

Nyní předvedu nástroj, pomocí kterého to můžete udělat. Tento nástroj automaticky generuje pravidelné a robustní

toString

metoda pro zadanou třídu, téměř eliminující čas strávený vývojem metody. Rovněž centralizuje

toString ()

formát. Pokud změníte formát, musíte znovu vygenerovat

toString

metody; je to však stále mnohem jednodušší než ruční změna stovek nebo tisíců tříd.

Snadná je také údržba vygenerovaného kódu. Pokud do kurzů přidáte více atributů, může být od vás vyžadováno provedení změn v toString metoda také. Od generace toString metody jsou automatizované, pro provedení změn stačí znovu spustit obslužný program ve třídě. To je jednodušší a méně náchylné k chybám než manuální přístup.

Kód

Účelem tohoto článku není vysvětlit Reflection API; následující kód předpokládá, že máte alespoň pochopení konceptů za Reflection. Můžete navštívit

Zdroje

část pro dokumentaci Reflection API. Obslužný program je napsán následovně:

balíček fareed.publications.utilities; import java.lang.reflect. *; public class ToStringGenerator {public static void main (String [] args) {if (args.length == 0) {System.out.println ("Zadejte název třídy jako argument příkazového řádku"); System.exit (0); } try {Class targetClass = Class.forName (args [0]); if (! targetClass.isPrimitive () && targetClass! = String.class) {Field fields [] = targetClass.getDeclaredFields (); Třída cSuper = targetClass.getSuperclass (); // Načítání výstupu super třídy ("StringBuffer buffer = new StringBuffer (500);"); // Konstrukce vyrovnávací paměti if (cSuper! = Null && cSuper! = Object.class) {output ("buffer.append (super.toString ());"); // Super třída's toString ()} for (int j = 0; j <fields.length; j ++) {output ("buffer.append (\" "+ fields [j] .getName () +" = \ "); "); // Připojit název pole if (fields [j] .getType (). IsPrimitive () || fields [j] .getType () == String.class) // Zkontrolovat primitivní nebo řetězcový výstup ("buffer.append ( toto. "+ pole [j] .getName () +"); "); // Přidejte hodnotu primitivního pole else {/ * NENÍ to primitivní pole, takže to vyžaduje kontrolu hodnoty NULL pro agregovaný objekt * / output ("if (this." + Fields [j] .getName () + "! = null)"); output ("buffer.append (this." + pole [j] .getName () + ".toString ());"); output ("else buffer.append (\" hodnota je null \ ");"); } // end of else} // end of for loop output ("return buffer.toString ();"); }} catch (ClassNotFoundException e) {System.out.println ("Třída nebyla nalezena v cestě ke třídě"); System.exit (0); }} soukromý statický void výstup (String data) {System.out.println (data); }} 

Výstupní kanál kódu

Formát kódu závisí také na požadavcích na projektové nástroje. Někteří vývojáři mohou raději mít kód v uživatelem definovaném souboru na disku. Ostatní vývojáři jsou s

system.out

konzole, která jim umožňuje ručně zkopírovat a vložit kód do skutečného souboru. Tyto možnosti prostě nechám na vás a použiji nejjednodušší metodu:

system.out

prohlášení.

Omezení přístupu

Tento přístup má dvě důležitá omezení. První je, že nepodporuje objekty obsahující cykly. Pokud objekt A obsahuje odkaz na objekt B, který pak obsahuje odkaz na objekt A, tento nástroj nebude fungovat. Tento případ však bude u mnoha projektů vzácný.

Druhým omezením je, že přidávání nebo odečítání členských proměnných vyžaduje regeneraci toString metoda. Jelikož je to nutné provést s nástrojem nebo bez něj, není to problém specifický pro tento přístup.

Závěr

V tomto článku jsem vysvětlil malý automatizační nástroj, který může skutečně zlepšit produktivitu vývojářů a hrát malou, ale důležitou roli při snižování celkových časových harmonogramů projektu.


Následné tipy

Po zveřejnění tohoto tipu jsem od čtenářů obdržel několik návrhů, jak vylepšit kód. V tomto pokračování vysvětlím, jak jsem aktualizoval nástroj na základě těchto návrhů a vlastních poznatků. Zdrojový kód pro tato vylepšení najdete v Zdrojích.

Vylepšení č. 1, které navrhla Sangeeta Varma

V mém původním kódu jsem nezpracoval typy polí pro objekt a primitivní datový typ; nový kód nyní zpracovává data pole. Kód však jde pouze do jednorozměrných polí a nebude fungovat u vícerozměrných polí. Nebyl jsem schopen přijít s obecným řešením tohoto problému, protože podle mého nejlepšího vědomí neexistuje žádné omezení počtu dimenzí pro datové typy v Javě (jediným omezením je dostupná paměť). Vítám jakoukoli zpětnou vazbu, kterou můžete nabídnout k řešení.

Vylepšení # 2, navrhl Chris Sanscraint

Původně jsem navrhoval tento nástroj pro vývojový čas a ne pro běhové prostředí. Povolení spuštění nástroje za běhu může být velmi užitečné, ale může trvat několik dalších cyklů CPU. Dumping / ladění objektu (základní použití toString ()) se obvykle provádí během doby vývoje a pro produkční prostředí je vypnutý. V některých případech toto vypnutí v produkčním prostředí nemusí být použitelné, protože některé projekty mohou používat toString () pro účely obchodní logiky. Navrhuji učinit toto rozhodnutí projekt od projektu.

Před vývojem tohoto nástroje jsem měl v mysli už tuto runtime flexibilitu. Nejprve jsem vyvinul samostatnou delegující třídu, která byla použita jakoukoli třídou klienta k vygenerování toString (). Třída jej vygenerovala pomocí volání metody jako vrátit ToStringGenerator.generateToString (toto), kde tento odkazuje na aktuální instanci třídy klienta a příkaz kódu je zapsán do toString () implementace metody. Ale tento přístup se nezdařil, protože Reflection API nemá schopnost získat hodnoty pro soukromé členy za běhu. Třída byla tedy užitečná pouze pro veřejné členy, což jsem nechtěl.

Ale pak pan Sanscraint poukázal na to, že stejný kód Reflection API získá hodnotu soukromých členů za běhu, když je kód zapsán metodou stejné třídy volajícího. Takže jsem aktualizoval nástroj, který se má používat za běhu, a navíc toString () metoda nebude nikdy nutné aktualizovat nebo upravovat pro odečtení nebo přidání jakýchkoli atributů v cílové třídě.

Vylepšení # 3, navrhl Eric Ye

Původně jsem používal tento předpona pro přístup k proměnným členů v generovaném kódu, ale pan Ye poukázal na to, že kód lze použít také ve statické metodě nebo dokonce k výstupu statických členů. Aktualizovaný kód tedy nyní zvládne členy třídy i instance. Mr. Ye také identifikoval chybu, která byla opravena v této verzi, která způsobila, že třída vygenerovala zbytečný kód pro třídy bez atributů.

Úpravy kódu

Po zpřístupnění utility runtime jsem byl frustrován tím, že jsem musel kopírovat / vkládat metody v každé třídě, což se stalo obtížným, protože nový kód se skládal z více metod.

Jedním z řešení by bylo vytvořit rozhraní / abstraktní základní třídu, která by alespoň vyřešila problém podpisů metod, ale stále by bylo nutné kopírovat / vložit. Řešení abstraktní základní třídy by také omezilo klienta v odvození z jiné třídy.

Vnitřní třída má však možnost přístupu k soukromým členům nadřazené třídy, takže reflexní kód běžící v rámci jejích metod by mohl také získat soukromé hodnoty. Takže jsem se rozhodl změnit nástroj na vnitřní třídu, kterou lze vložit do jakékoli třídy nadřazeného klienta. Také jsem poskytl ToStringGeneratorExample.java, který používá ToStringGenerator.java jako vnitřní třídu k implementaci toString () metoda.

Na závěr chci poděkovat těm lidem, kteří nabídli své návrhy na zlepšení tohoto přístupu.

Syed Fareed Ahmad je programátor, designér a architekt Java v Lahore v Pákistánu. Podílí se na vývoji řešení elektronického obchodování založených na prostředí Java (Servlets, JSP a EJB), WebSphere a XML.

Další informace o tomto tématu

  • Pro následný zdrojový kód

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/08/jw-javatip99.zip

  • Reflexní dokumentace na webových stránkách společnosti Sun

    //java.sun.com/products/jdk/1.1/docs/guide/reflection/index.html

  • Zobrazit všechny předchozí Tipy pro Java a odešlete vlastní

    //www.javaworld.com/javatips/jw-javatips.index.html

Tento příběh, „Java Tip 99: Automate toString () creation“, původně publikoval JavaWorld.