分享
 
 
 

使toString()的创建自动化

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

有经验的 Java 开发人员都知道写好 toString 方法很重要。对可用人类理解的形式查看的对象进行记录和调试都相当容易;当处理分布式应用程序时尤其是这样。不幸的是,为许多类实现 toString 方法可能相当耗时 -- 尤其是对于那些具有许多属性的类。因为 toString 的行为相当规则,所以最好使创建该方法的任务能够自动完成。本文提供的这个实用程序可以帮助您实现这一点,同时也减轻了您在开发时的负担。

参与大项目的开发人员通常要花数个小时编写有用的 toString 方法。即便不为每个类都提供属于它们自己的 toString 方法,但每个数据容器都必须有自己的 toString 方法。让每个开发人员按他们自己的方法编写 toString 方法可能会造成混乱;每个开发人员无疑都会提出一种唯一的格式。结果,在调试过程中使用这样的输出将增添不必要的麻烦,而且也没有什么好处。因此,每个项目都应该为 toString 方法规定一种单一的格式,并使它们的创建自动化。

使 toString 的创建自动化

我下面将演示一个实用程序,您可用它来实现 toString 的自动创建。这个工具会自动为指定的类生成一个规则的、强健的 toString 方法,几乎消除了用于开发该方法的时间。它还对 toString() 的格式进行集中管理。如果您更改了格式,则必须重新生成 toString 方法;但是,这仍然比手动更改成百上千个类要容易得多。

对生成的代码进行维护也很容易。如果您在类中添加了更多的属性,则您也可能需要对 toString 方法作一些修改。因为 toString 方法是自动生成的,所以您只须再次对该类运行这个实用程序来完成更改。这比手动方法更简单,而且犯错误的可能性也较小。

代码

本文无意解释 Reflection API;下面的代码假定您已理解 Reflection 的基本概念。要查看 Reflection API 的文档,您可以访问参考资源部分。实用程序的源代码如下所示:

package fareed.publications.utilities;

import java.lang.reflect.*;

public class ToStringGenerator

{

public static void main(String[] args)

{

if (args.length == 0)

{

System.out.println("Provide the class name as the command line argument");

System.exit(0);

}

try {

Class targetClass = Class.forName(args[0]);

if (!targetClass.isPrimitive() && targetClass != String.class)

{

Field fields[] = targetClass.getDeclaredFields();

Class cSuper = targetClass.getSuperclass(); // 检索超类

output("StringBuffer buffer = new StringBuffer(500);"); // 构造缓冲区

if (cSuper != null && cSuper != Object

for (int j = 0; j < fields.length; j++) {

output("buffer.append(\"" + fields[j].getName() + " = \");"); // 附加域名称

if (fields[j].getType().isPrimitive() || fields[j].getType() == String.class) // 检查基本数据类型或字符串类型

output("buffer.append(this." + fields[j].getName() + ");"); // 附加基本数据类型域的值

else

{

/* 它“不是”基本数据类型域,所以需要检查聚集对象的 NULL 值 */

output("if ( this." + fields[j].getName() + "!= null )" );

output("buffer.append(this." + fields[j].getName() + ".toString());");

output("else buffer.append(\"value is null\"); ");

} // else 结束

} // 循环结束

output("return buffer.toString();");

}

} catch (ClassNotFoundException e) {

System.out.println("Class not found in the class path");

System.exit(0);

}

}

private static void output(String data)

{

System.out.println(data);

}

}

代码输出通道

代码的格式还取决于您的项目工具需求。某些开发人员可能喜欢将这些代码存入磁盘上用户定义的文件中。而另一些开发人员对 system.out 控制台就很满意,他们可以利用控制台手动将这些代码复制或嵌入实际的文件中。我将这些选择权留给您,本文只使用最简单的方法:system.out 语句。

这种方法的局限性

这种方法有两个明显的局限性。第一个局限性是它不支持对象的循环包含。如果对象 A 包含对象 B 的一个引用,对象 B 又包含对象 A 的一个引用,则这个工具无法处理。但是,对于许多项目而言,这种情况很少出现。

第二个局限性是添加或减少成员变量之后要求重新生成 toString 方法。因为不管用不用这个工具都需要完成这一步,所以这不是工具特有的问题。

小结

在本文中,我说明了一个小型的自动实用程序,它可以真正提高开发人员的效率,就整个项目的工期而言,它起着很小但很重要的作用。

补充技巧

这篇技巧发表以后,我收到读者关于改进这段代码的几点建议。在这部分补充内容中,我将说明如何根据这些建议以及我自己的见解改进这个实用程序。在参考资源部分可找到这些改进的源代码。

改进 在最初的代码中,我没有处理包含对象和基本数据的数组类型;现在新代码可以处理数组数据。但是,这段代码只能处理一维数组,而不能处理多维数组。我现在还无法提供这个问题的一般解决方案,因为就我所知,Java 对数据类型的维数没有限制(只受可用内存的限制)。我欢迎您提供解决方案。

改进 最初我建议将这个工具用于开发时环境,而不是运行时环境。允许这个工具在运行时运行很方便,但可能会占用更多的 CPU 周期。但是,对象转储/调试(toString() 的基本用途)通常是在开发时进行,而在生产环境中要将其关闭。在某些情况下,如果某些项目可能将 toString() 用于业务逻辑,则在生产环境中就可能不能关闭转储/调试。我建议您根据具体项目决定是否将其关闭。

在开发这个实用程序之前,我已经想到了这种运行时灵活性。首先,我开发了一个单独的授权类,客户端的类用它来生成 toString()。这个类利用类似 return ToStringGenerator.generateToString(this) 的一个方法调用生成 toString,其中 this 指向客户端类的当前实例,这条语句放在 toString() 方法实现中。但这个方法失败了,因为 Reflection API 在运行时无法获取私有成员的值。因此这个类只能用于公用成员,这不是我所希望的。

但后来 Sanscraint 先生指出,如果将这段代码放在同一个调用类的一个方法中,则同样的 Reflection API 代码就可以获取私有成员的值。因此,我修改了这个实用程序以便在运行时使用,另外,即使在目标类中新增或减少了任何属性,您也不必更新或编辑 toString() 方法。

改进 最初我用 this 前缀来访问生成代码中的成员变量,但 Ye 先生指出,这段代码也可用于静态方法,甚至可用来输出静态成员。这样,更新后的代码现在既可处理类成员,也可处理实例成员。Ye 先生还指出一个错误(在此版本中已修正),这个错误导致为无属性的类生成毫无用处的代码。

代码修改

在使这个实用程序可用于运行时以后,我不得不在每个类中复制/粘贴这些方法,因为新代码由多个方法组成,所以这变得很困难,我对此感到很沮丧。

一种解决方案是创建一个接口/抽象基类,这至少解决了方法签名问题,但仍然需要进行复制/粘贴。抽象基类还使客户类无法从另一个类继承出。

但是,内部类可以访问父类的私有成员,这样在内部类的方法中运行的 Reflection 代码也可以获取私有值。因此我决定将这个实用程序改为内部类,它可被插入客户的任何父类中。我还提供了 ToStringGeneratorExample.java,它将 ToStringGenerator.java 用作内部类来实现 toString() 方法。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有