有經驗的 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 ==
)
{
System
out
println(
Provide the class name as the command line argument
);
System
exit(
);
}
try {
Class targetClass = Class
forName(args[
]);
if (!targetClass
isPrimitive() && targetClass != String
class)
{
Field fields[] = targetClass
getDeclaredFields();
Class cSuper = targetClass
getSuperclass(); // 檢索超類
output(
StringBuffer buffer = new StringBuffer(
);
); // 構造緩沖區
if (cSuper != null && cSuper != Object
for (int j =
; 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(
);
}
}
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 ToStringGeneratorgenerateToString(this) 的一個方法調用生成 toString其中 this 指向客戶端類的當前實例這條語句放在 toString() 方法實現中但這個方法失敗了因為 Reflection API 在運行時無法獲取私有成員的值因此這個類只能用於公用成員這不是我所希望的
但後來 Sanscraint 先生指出如果將這段代碼放在同一個調用類的一個方法中則同樣的 Reflection API 代碼就可以獲取私有成員的值因此我修改了這個實用程序以便在運行時使用另外即使在目標類中新增或減少了任何屬性您也不必更新或編輯 toString() 方法
改進 最初我用 this 前綴來訪問生成代碼中的成員變量但 Ye 先生指出這段代碼也可用於靜態方法甚至可用來輸出靜態成員這樣更新後的代碼現在既可處理類成員也可處理實例成員Ye 先生還指出一個錯誤(在此版本中已修正)這個錯誤導致為無屬性的類生成毫無用處的代碼
代碼修改
在使這個實用程序可用於運行時以後我不得不在每個類中復制/粘貼這些方法因為新代碼由多個方法組成所以這變得很困難我對此感到很沮喪
一種解決方案是創建一個接口/抽象基類這至少解決了方法簽名問題但仍然需要進行復制/粘貼抽象基類還使客戶類無法從另一個類繼承出
但是內部類可以訪問父類的私有成員這樣在內部類的方法中運行的 Reflection 代碼也可以獲取私有值因此我決定將這個實用程序改為內部類它可被插入客戶的任何父類中我還提供了 ToStringGeneratorExamplejava它將 ToStringGeneratorjava 用作內部類來實現 toString() 方法
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19523.html