將 Applet 參數化通常是項繁瑣工作,需在 init() 方法中添加大量重複代碼。上月介紹了如何利用類反射機制將代碼縮減為一行,但未說明如何實現陣列初始化。本文將詳述使陣列自動初始化的細節。
假定讀者熟悉類反射機制(java.lang.reflect 套件),它能檢查類結構、提供欄位、方法、存取修飾符等運行時資訊。上一技巧只能處理基本型別陣列和字串陣列,考慮到任何對象最終都由基本資料型別和字串構建,這不構成大限制,且易擴展至其他型別。
<PARAM NAME="myArray" VALUE="element1 element2 ... elementN">
元素間定界符為空格。
<PARAM NAME="myMatrix" VALUE="element11 element12 element13
element21 element22 element23
element31 element32 element33">
行間定界符為 | 符號。支援不規則陣列(各行長度不同),如帕斯卡三角形:
<PARAM NAME="pascalTriangle" VALUE=" 1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1">
若程式設計師預先為某行分配記憶體(如 pascalTriangle[3] = new int[2]),方法只提取前兩個元素。
Foo 時,同時定義 Foo[] 自動結合陣列層次結構Object[] 和基本型別陣列都是 Object 子類用符號處理器內容填充一維陣列,符號轉換為陣列內容型別:
private static void fillOneDimensionalArray(Object array, StringTokenizer elementTokens)
throws IllegalAccessException {
if (array != null && elementTokens != null && array.getClass().isArray()) {
Class componentType = array.getClass().getComponentType();
int numElements = elementTokens.countTokens();
for (int j = 0; j < Array.getLength(array) && j < numElements; j++) {
if (componentType.equals(boolean.class))
Array.setBoolean(array, j, Boolean.valueOf(elementTokens.nextToken().trim()).booleanValue());
else if (componentType.equals(byte.class))
Array.setByte(array, j, Byte.valueOf(elementTokens.nextToken().trim()).byteValue());
// ... 其他基本型別處理 ...
else if (componentType.equals(String.class))
Array.set(array, j, elementTokens.nextToken());
}
}
}
使用 Class.getComponentType() 獲取陣列元素型別,Array.setXxx() 相當於 ((type[])obj)[i] = datum。
在 Util.initializeApplet(Applet, String) 中添加條件判斷,快取陣列欄位並初始化:
componentType.isPrimitive() || componentType.equals(String.class),用 StringTokenizer 解析元素,自動分配記憶體componentType.isArray() 且元素型別為基本型別或 String,用 | 分隔子陣列,遞迴調用 fillOneDimensionalArray 初始化每行關鍵語句 Object subArray = ((Object[])array)[j]; 從二維陣列提取第 j 行,因任何一維陣列最近公共超類為 Object。
本技巧說明如何利用類反射機制減輕開發可配置 Applet 的負擔,陣列提取例程補充了參數化技術。使用一個完成所有參數提取工作的方法,免除反覆輸入 getParameter 的乏味工作。