Java съвет 99: Автоматизирайте създаването на toString ()

Разработчиците, работещи по големи проекти, обикновено прекарват часове в писане на полезни toStringметоди. Дори всеки клас да не получи собствен toStringметод, всеки клас контейнер за данни ще го направи. Разрешаването на всеки разработчик да пише toStringпо свой собствен начин може да доведе до хаос; всеки разработчик несъмнено ще излезе с уникален формат. В резултат на това използването на изхода по време на отстраняване на грешки става по-трудно от необходимото, без очевидна полза. Следователно всеки проект трябва да стандартизира един формат за toStringметоди и след това да автоматизира тяхното създаване.

Автоматизирайте toString

Сега ще демонстрирам програма, с която можете да направите точно това. Този инструмент автоматично генерира редовен и надежден

toString

метод за определен клас, като почти елиминира времето, прекарано в разработването на метода. Той също така централизира

toString()

формат. Ако промените формата, трябва да генерирате

toString

методи; това обаче все още е много по-лесно от ръчната промяна на стотици или хиляди класове.

Поддържането на генерирания код също е лесно. Ако добавите още атрибути в класовете, може да се наложи да направите промените и в toStringметода. Тъй като генерирането на toStringметоди е автоматизирано, трябва само да стартирате помощната програма на класа отново, за да направите промените си. Това е по-просто и по-малко склонно към грешки от ръчния подход.

Кодът

Тази статия няма за цел да обясни API за отразяване; следващият код предполага, че имате поне разбиране на концепциите зад Reflection. Можете да посетите

Ресурси

раздел за документацията на API за отразяване. Помощната програма е написана по следния начин:

пакет fareed.publications.utilities; импортиране на java.lang.reflect. *; публичен клас ToStringGenerator {public static void main (String [] args) {if (args.length == 0) {System.out.println ("Предоставете името на класа като аргумент на командния ред"); System.exit (0); } опитайте {Class targetClass = Class.forName (аргументи [0]); if (! targetClass.isPrimitive () && targetClass! = String.class) {Полета на поле [] = targetClass.getDeclaredFields (); Клас cSuper = targetClass.getSuperclass (); // Извличане на изхода на супер клас ("StringBuffer буфер = нов StringBuffer (500);"); // Конструкция на буфера if (cSuper! = Null && cSuper! = Object.class) {output ("buffer.append (super.toString ());"); // toString ()} на супер клас за (int j = 0; j <fields.length; j ++) {output ("buffer.append (\" "+ полета [j].getName () + "= \"); "); // Добавяне на име на поле ако (полета [j] .getType (). isPrimitive () || полета [j] .getType () == String.class) // Проверете за примитивен или низ изход ("buffer.append (this." + Fields [j] .getName () + ");"); // Добавете стойността на примитивното поле else {/ * НЕ е примитивно поле, така че това изисква проверка за NULL стойност за агрегирания обект * / output ("if (this." + fields [j] .getName () + "! = null)"); output ("buffer.append (this." + полета [j] .getName () + ".toString ());"); output ("else buffer.append (\" value is null \ ");");} // край на друго} // край на за изход за цикъл ("return buffer.toString ();");}} catch (ClassNotFoundException e) {System.out.println ("Class not found in the class path"); System.exit (0);}} частни статични изходни данни (String данни) {System.out.println (данни); }}

Изходният канал на кода

Форматът на кода също зависи от изискванията на инструмента за вашия проект. Някои разработчици може да предпочетат да имат кода в дефиниран от потребителя файл на диска. Други разработчици са доволни от

system.out

конзола, която им позволява да копират и вграждат кода в действителния файл ръчно. Просто оставям тези опции на вас и използвам най-простия метод:

system.out

изявления.

Ограничения на подхода

Има два важни ограничения за този подход. Първият е, че не поддържа обекти, съдържащи цикли. Ако обект A съдържа препратка към обект B, който след това съдържа препратка към обект A, този инструмент няма да работи. Този случай обаче ще бъде рядък за много проекти.

Второто ограничение е, че добавянето или изваждането на променливи на членове изисква регенериране на toStringметода. Тъй като това трябва да се направи с или без инструмента, това не е проблем, специфичен за този подход.

Заключение

В тази статия обясних малка помощна програма за автоматизация, която наистина може да подобри производителността на разработчиците и да играе малка, но важна роля в намаляването на общите срокове на проекта.


Последващи съвети

След публикуването на този съвет получих няколко предложения от читателите как да подобря кода. В това последващо описание обяснявам как съм актуализирал помощната програма въз основа на тези предложения и моите собствени прозрения. Можете да намерите изходния код за тези подобрения в Ресурси.

Подобрение # 1, предложено от Sangeeta Varma

В оригиналния си код не обработвах типовете масиви за обекта и примитивния тип данни; новият код сега обработва данните от масива. Кодът обаче се издига само до масиви с едно измерение и няма да работи за масиви с множество измерения. Не успях да измисля общо решение за този проблем, тъй като доколкото ми е известно, няма ограничение за броя измерения за типовете данни в Java (единственото ограничение е наличната памет). Приветствам всяка обратна връзка, която можете да предложите за решение.

Подобрение # 2, предложено от Chris Sanscraint

Първоначално предложих помощната програма за времето за разработка, а не за средата на изпълнение. Разрешаването на помощната програма да работи по време на изпълнение може да бъде много удобно, но може да отнеме още няколко цикъла на процесора. Обаче обектното изхвърляне / отстраняване на грешки (основно използване на toString()) обикновено се извършва по време на разработката и се изключва за производствената среда. В някои случаи това изключване в производствената среда може да не е приложимо, тъй като някои проекти могат да използват toString()за целите на бизнес логиката. Предлагам да вземете това решение за всеки отделен проект.

Преди да разработя тази програма, вече имах тази гъвкавост по време на работа в съзнанието си. Първо, разработих отделен делегиращ клас, който се използва от всеки клиентски клас за генериране на toString(). Класът го е генерирал с помощта на извикване на метод като return ToStringGenerator.generateToString(this), където thisсочи към текущия екземпляр на клиентския клас и кодовият оператор се записва в toString()изпълнението на метода. Но този подход се провали, тъй като API за отразяване няма способността да получава стойностите за частните членове по време на изпълнение. Така че класът беше полезен само за публични членове, което аз не исках.

Но тогава г-н Sanscraint посочи, че същият код на API за отразяване получава стойността на частните членове по време на изпълнение, когато кодът е написан в метод от същия клас на повикващия. Така че актуализирах помощната програма, която да се използва по време на изпълнение, и освен това toString()методът никога няма да се нуждае от актуализиране или редактиране за изваждане или добавяне на каквито и да било атрибути в целевия клас.

Подобрение # 3, предложено от Ерик Йе

Първоначално използвах thisпрефикса за достъп до променливите-членове в генерирания код, но г-н Ye посочи, че кодът може да се използва и в статичен метод или дори за извеждане на статични членове. Така че актуализираният код вече може да обработва членове на клас и екземпляр. Г-н Йе също идентифицира грешка, която е коригирана в тази версия, която е накарала класа да генерира безполезен код за безатрибутни класове.

Code modifications

After making the utility runtime-enabled, I was frustrated by having to copy/paste the methods in each class, which became difficult since the new code was comprised of multiple methods.

One solution would be to create an interface/abstract base class that would at least solve the problem of method signatures, but copy/paste would still be required. The abstract base class solution would also restrict the client from deriving from another class.

Вътрешният клас обаче има възможност за достъп до частните членове на родителския клас, така че кодът за отражение, работещ в рамките на неговите методи, също може да получи частните стойности. Затова реших да променя помощната програма във вътрешен клас, който може да бъде вмъкнат във всеки родителски клиентски клас. Също така съм предоставил ToStringGeneratorExample.java, който използва ToStringGenerator.java като вътрешен клас за реализиране на toString()метода.

И накрая, искам да благодаря на хората, които предложиха своите предложения за подобряване на този подход.

Syed Fareed Ahmad е програмист, дизайнер и архитект на Java в Лахор, Пакистан. Той участва в разработването на Java- (Servlets, JSP и EJB), WebSphere- и XML-базирани решения за електронен бизнес.

Научете повече за тази тема

  • За последващия изходен код

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

  • Документация за размисъл на уебсайта на Sun

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

  • Вижте всички предишни съвети за Java и изпратете свои собствени

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

Тази история „Java съвет 99: Автоматизиране на създаването на toString ()“ първоначално е публикувана от JavaWorld.