Last Updated on 2019-07-21 by OneJar99
在上一篇介紹了 ReflectionToStringBuilder 的基本用法,這篇進一步示範 ReflectionToStringBuilder 提供的另一個功能:排除指定名稱的欄位。
自訂類別裡可能有非常多成員變數,在印出自訂類別的內容來進行 log 儲存時,我們需要的也許只是其中較關鍵的幾項,印出所有變數不僅不具意義,甚至是浪費儲存空間。這時候就可以使用 ReflectionToStringBuilder 的 toStringExclude 功能,來排除不需要的欄位名稱。
ReflectionToStringBuilder 的 excludeFieldNames 變數
API 規格文件可以看到 ReflectionToStringBuilder 擁有一個成員變數「excludeFieldNames 」,顯示 ReflectionToStringBuilder 在印出物件的成員變數內容時,會以成員變數的名稱去比對這個陣列裡的名單,判斷是否排除。
以下示範如何使用。
ReflectionToStringBuilder 的 toStringExclude 基本範例
以下面這個例子作示範:
EX1: 尚未排除任何欄位名稱
public class Demo1
{
class Mobile extends Device
{
private String brand = "Apple";
private String os = "iOS 10";
private String type = "iPhone 7";
private String owner;
}
class Device extends Product
{
private String serialNumber = "MIT00001";
}
class Product extends BaseObj
{
private int price = 30000;
}
class BaseObj
{
@Override
public String toString()
{
return ReflectionToStringBuilder.toString( this );
}
}
@Test
public void ex01_ReflectionToStringBuilder_example()
{
Mobile myPhone = new Mobile();
System.out.println( myPhone );
}
}
輸出結果:
com.pcc.demo.reflectToString.ex2.Demo1$Mobile@7aec35a[brand=Apple,os=iOS 10,type=iPhone 7,owner=<null>,serialNumber=MIT00001,price=30000]
在沒有設定排除任何屬性名稱之前,會印出該 Mobile 物件 myPhone 擁有的 6 個屬性,包含繼承自父類別的 serialNumber 和 price 屬性。
這時候我們希望排除 price 和 os 兩個屬性,寫法如下:
EX2: 排除指定名稱的屬性
/* Example 1 */
System.out.println( ReflectionToStringBuilder.toStringExclude( myPhone, "price", "os" ) );
/* Example 2 */
List<String> excludeFieldNameList = new ArrayList<>();
excludeFieldNameList.add( "price" );
excludeFieldNameList.add( "os" );
System.out.println( ReflectionToStringBuilder.toStringExclude( myPhone, excludeFieldNameList ) );
/* Example 3 */
String[] excludeFieldNameAry = new String[] { "price", "os" };
ReflectionToStringBuilder builder = new ReflectionToStringBuilder( myPhone );
builder.setExcludeFieldNames( excludeFieldNameAry );
System.out.println( builder.toString() );
上面 Example 1~3 只是寫法不同,效果一樣:
com.pcc.demo.reflectToString.ex2.Demo1$Mobile@7aec35a[brand=Apple,type=iPhone 7,owner=<null>,serialNumber=MIT00001]
ReflectionToStringBuilder 的 toStringExclude 進階範例
再看另一個例子,Person 類別包含成員變數是 Mobile 類別,我們希望排除 favoriteOs、os、type 三個屬性名稱。
如果照以下寫法,預期會得到什麼?
EX3: 自訂類別的成員變數包含其他自訂類別
public class Demo2
{
class Mobile extends BaseObj
{
private String brand = "Apple";
private String os = "iOS 10";
private String type = "iPhone 7";
private String owner;
}
class Person extends BaseObj
{
private String name = "OneJar99";
public String[] favoriteFruit;
public List<String> favoriteOs;
public Mobile mobile;
}
class BaseObj
{
@Override
public String toString()
{
return ReflectionToStringBuilder.toString( this );
}
}
@Test
public void ex03_ReflectionToStringBuilder_excludeExample()
{
String[] fruitAry = new String[] { "Apple", "Banana", "Orange" };
List<String> osList = new ArrayList<>();
osList.add( "iOS" );
osList.add( "Android" );
Mobile myPhone = new Mobile();
Person me = new Person();
me.favoriteFruit = fruitAry;
me.favoriteOs = osList;
me.mobile = myPhone;
System.out.println( "============ result 1 ===============" );
System.out.println( me );
System.out.println( "============ result 2 ===============" );
System.out.println( ReflectionToStringBuilder.toStringExclude( me, "favoriteOs", "os", "type" ) );
}
}
輸出結果:
============ result 1 ===============
com.pcc.demo.reflectToString.ex2.Demo2$Person@7aec35a[name=OneJar99,favoriteFruit={Apple,Banana,Orange},favoriteOs=[iOS, Android],mobile=com.pcc.demo.reflectToString.ex2.Demo2$Mobile@579bb367[brand=Apple,os=iOS 10,type=iPhone 7,owner=<null>]]
============ result 2 ===============
com.pcc.demo.reflectToString.ex2.Demo2$Person@7aec35a[name=OneJar99,favoriteFruit={Apple,Banana,Orange},mobile=com.pcc.demo.reflectToString.ex2.Demo2$Mobile@579bb367[brand=Apple,os=iOS 10,type=iPhone 7,owner=<null>]]
沒錯,os 和 type 屬性並沒有被排除,依然印出來。
這是因為上述寫法是針對「me」這個 Person 物件去進行排除,Person 物件裡的 mobile 屬性雖然透過 reference 參考到另一個 Mobile 物件,但他們仍然是兩個不同的物件實體,ReflectionToStringBuilder 的排除效果與 Mobile 物件無關。而 Person 並沒有名為 os 和 type 的屬性,等同於無效。
那該怎麼達到期望的效果呢?
通常來說,每個自訂類別裡那些屬性是需要印出的關鍵屬性,在開發設計階段就會決定。
每個類別都可能有需要排除的屬性,因此在 BaseObj 類別增加一個變數,負責記住每個自訂類別需要被排除的欄位名稱,在覆寫的 toString() 裡調整呼叫的 ReflectionToStringBuilder API,加入排除效果。
接著,由每個自訂類別透過建構子,個別依需求實作需要排除的屬性。
EX4: 設定每個自訂類別需要被排除的屬性名稱
public class Demo3
{
class Mobile extends BaseObj
{
private String brand = "Apple";
private String os = "iOS 10";
private String type = "iPhone 7";
private String owner;
public Mobile()
{
excludeFieldList.add( "os" ); // 在建構子實作要排除的屬性名稱
excludeFieldList.add( "type" );
}
}
class Person extends BaseObj
{
private String name = "OneJar99";
public String[] favoriteFruit;
public List<String> favoriteOs;
public Mobile mobile;
public Person()
{
excludeFieldList.add( "favoriteOs" ); // 在建構子實作要排除的屬性名稱
}
}
class BaseObj
{
protected List<String> excludeFieldList = new ArrayList<>(); // 紀錄需要排除的屬性名稱
@Override
public String toString()
{
return ReflectionToStringBuilder.toStringExclude( this, excludeFieldList ); // 改呼叫 toStringExclude()
}
}
@Test
public void ex04_ReflectionToStringBuilder_excludeExample()
{
String[] fruitAry = new String[] { "Apple", "Banana", "Orange" };
List<String> osList = new ArrayList<>();
osList.add( "iOS" );
osList.add( "Android" );
Mobile myPhone = new Mobile();
Person me = new Person();
me.favoriteFruit = fruitAry;
me.favoriteOs = osList;
me.mobile = myPhone;
System.out.println( me );
}
}
輸出結果:
com.pcc.demo.reflectToString.ex2.Demo3$Person@42110406[name=OneJar99,favoriteFruit={Apple,Banana,Orange},mobile=com.pcc.demo.reflectToString.ex2.Demo3$Mobile@255316f2[brand=Apple,owner=<null>,excludeFieldList=[os, type]],excludeFieldList=[favoriteOs]]
如此成功達到排除 Mobile 物件的 os 和 type 屬性。
但由於在 BaseObj 類別增加 excludeFieldList 變數,而 BaseObj 被 Person、Mobile 類別繼承,因此這兩個物件都增加印出 excludeFieldList。但 excludeFieldList 變數的存在只是程式開發上的需求,並不具備紀錄 log 的意義,理想上也應該對其進行排除:
EX5: 將 BaseObj 類別的 excludeFieldList 屬性也加入排除名單
class BaseObj
{
protected List<String> excludeFieldList = new ArrayList<>();
public BaseObj()
{
excludeFieldList.add( "excludeFieldList" );
}
@Override
public String toString()
{
return ReflectionToStringBuilder.toStringExclude( this, excludeFieldList );
}
}
輸出結果:
com.pcc.demo.reflectToString.ex2.Demo3$Person@277050dc[name=OneJar99,favoriteFruit={Apple,Banana,Orange},mobile=com.pcc.demo.reflectToString.ex2.Demo3$Mobile@531d72ca[brand=Apple,owner=<null>]]