20162330 第三周 藍(lán)墨云班課 泛型類-Bag 練習(xí)
目錄
題目及要求
- 代碼運(yùn)行在命令行中,路徑要體現(xiàn)學(xué)號(hào)信息,IDEA中,偽代碼要體現(xiàn)個(gè)人學(xué)號(hào)信息;
- 參見(jiàn)Bag的UML圖,用Java繼承BagInterface實(shí)現(xiàn)泛型類Bag,并對(duì)方法進(jìn)行單元測(cè)試(JUnit),測(cè)試要涵蓋正常、異常情況、邊界情況;
- 課上提交測(cè)試代碼和測(cè)試運(yùn)行的結(jié)果截圖,截圖要求全屏截圖,包含自己的學(xué)號(hào)信息,否則無(wú)效;測(cè)試Bag類的代碼中至少包含一個(gè)自定義類如Student;
- 課下完成碼云上代碼的上傳。
- 【附】Bag的UML圖:

思路分析
-
首先自定義一個(gè)Bag類,使用
public class Bag<T> implements BagInterface<T>實(shí)現(xiàn)給出接口的所有方法框架,之后在里面通過(guò)泛型類型 T 自定義一個(gè)數(shù)組,通過(guò)填充方法來(lái)完善Bag類??紤]到給出的接口含有add、remove、isEmpty等方法,如果使用 List 創(chuàng)建對(duì)象實(shí)現(xiàn)有些簡(jiǎn)單,所以我選擇了使用 Object 類定義數(shù)組實(shí)現(xiàn)這些方法。
在 Bag 類的 UML類圖 中,我們可以清楚地知道每個(gè)方法的返回類型、參數(shù)及需要實(shí)現(xiàn)的功能,大部分功能的實(shí)現(xiàn)比較框架化,比如:getCurrentSize()、isEmpty、add(T newEntry)、remove()等,其方法填充內(nèi)容基本符合以下框架:public <返回類型> <函數(shù)名>(<參數(shù)>){ //初始化返回值參數(shù)對(duì)象 //循環(huán)(遍歷、修改元素) //條件(何時(shí)修改、賦值) //返回值(參數(shù)) }- 之所以能用 循環(huán) + 條件 的框架是因?yàn)檫@些方法基本都涉及遍歷環(huán)節(jié),我也使用了幾種不同的遍歷方式。
需要注意的是最后一個(gè)方法toArray(),這個(gè)方法相對(duì)陌生,我查找了API,關(guān)鍵句如下:
按 適當(dāng)順序 返回包含此列表中所有元素的數(shù)組;
如果指定的數(shù)組能容納隊(duì)列,并有剩余的空間,那么會(huì)將數(shù)組中緊接 collection 尾部的元素設(shè)置為 null。- 所謂的“適當(dāng)順序”,可以理解為不同與原來(lái)數(shù)組排列元素的順序,我又繼續(xù)看了UML類圖中的這一方法的注釋:
A new array of entries currently in the bag.
- 我的思路是將原來(lái)數(shù)組中的空值元素都填補(bǔ)為同一類型的相同的值,然后再重新返回這個(gè)數(shù)組,這樣就可以通過(guò)一個(gè)含空值的數(shù)組調(diào)用此方法之后的元素容量進(jìn)行單元測(cè)試。實(shí)現(xiàn)代碼如下:
/* Shows all objects in a new array of food bag entries. */ public T[] toArray() { int j = food.length - 1; for (int i = 0;i<food.length;i++) { food[j] = "apple"; if (food[i] == null) { //將空值全部填補(bǔ) food[i] = food[j]; j--; } } return (T[]) food; } - 之所以能用 循環(huán) + 條件 的框架是因?yàn)檫@些方法基本都涉及遍歷環(huán)節(jié),我也使用了幾種不同的遍歷方式。
遇到的問(wèn)題和解決過(guò)程
-
【問(wèn)題】在使用Junit測(cè)試
add(T newEntry)方法時(shí),Junit測(cè)試異常:

-
【解決方法】我仔細(xì)看了一下自己寫的 add 方法:
public boolean add(T newEntry) { boolean boo = false; for (Object i : food) { if (i == null) { i = newEntry; //添加到第一個(gè)空值位置 boo = true; break; } } return boo; } -
由于使用foreach遍歷比較簡(jiǎn)單,我就沒(méi)有考慮其他問(wèn)題。IDEA的提示,給 i 重新賦值的那條語(yǔ)句中的 i 是多余的:

-
我就想不通為什么是多余的,于是又開(kāi)始檢查 foreach 的結(jié)構(gòu)框架:
for(<元素類型> <元素變量> : <遍歷對(duì)象>){ 引用元素變量的相關(guān)語(yǔ)句; } -
修改了幾次數(shù)組類型之后還是不對(duì),于是我認(rèn)為是foreach方法出現(xiàn)問(wèn)題,就做了一個(gè)foreach的測(cè)試類,發(fā)現(xiàn)果然是foreach方法的問(wèn)題,同樣的修改賦值語(yǔ)句,使用for循環(huán)遍歷就正常,使用foreach就不能進(jìn)行相應(yīng)賦值:

-
剛開(kāi)始設(shè)斷點(diǎn),我并沒(méi)有注意觀察細(xì)節(jié),只是跟著步驟走了一遍,又跟蹤了一遍才發(fā)現(xiàn)給 i 賦值的語(yǔ)句好像并沒(méi)有效果,這一句出現(xiàn)了問(wèn)題。我又仔細(xì)地跟蹤了一遍,發(fā)現(xiàn)add元素的地址和原來(lái)空值的地址不一樣,而最后數(shù)組添加的是空值的地址:
所以總結(jié)起來(lái)就是,i 原來(lái)對(duì)應(yīng)的地址就不是指向數(shù)組元素的,所以只要用其他循環(huán)(for循環(huán))替代即可正常修改原數(shù)組的元素:

如果一定要使用foreach修改數(shù)組元素的話,那只能再另外加一層循環(huán):
public boolean add(T newEntry) { boolean boo = false; for (Object i : food) { if (i == null) { //foreach不能修改數(shù)組元素 for (int j = 0; j < food.length; j++) { //重新使用for循環(huán)賦值 if (food[j] == i) { food[j] = newEntry; //添加到第一個(gè)空值位置 break; } } boo = true; break; } } return boo; } -
至于我的驗(yàn)證是否正確,我又查找了相關(guān)資料,一種說(shuō)法是:
foreach結(jié)構(gòu)中的元素變量是個(gè)基本數(shù)據(jù)類型,在遍歷時(shí)不指向數(shù)組元素的地址,它只代表數(shù)字它自己。
-
還有一個(gè)實(shí)例可以更好地證明我的驗(yàn)證:
for (Integer temp : list) { if (temp == 1) { temp = temp * 2; } } -
根據(jù)oracle的官方文檔,正式翻譯應(yīng)該如下:
for (Iterator i = list.iterator(); i.hasNext(); ) { float i0 = (Integer)i.next(); if(i0 == 1) i0 = i0*2; } -
所以foreach中的 temp變量只是一個(gè)局部變量(i0),而且還是集合中元素的一個(gè)副本,并不是元素本身。這樣才導(dǎo)致輸出“修改過(guò)的數(shù)組”時(shí),仍然輸出原數(shù)組。
-
綜上所述,我的驗(yàn)證正確,foreach只適合遍歷數(shù)組,在實(shí)現(xiàn)涉及到修改數(shù)組元素的功能時(shí),不宜使用,會(huì)造成賦值失敗。
代碼實(shí)現(xiàn)及托管鏈接
-
這里只貼出Bag類代碼和運(yùn)行成功截圖,其余代碼見(jiàn)相關(guān)代碼托管鏈接:
【BagInterface類】
【Bag類】
【BagTest類】 -
Bag類如下:
/**
* A finite number of objects,not necessarily distinct,in no particular order,
* and having the same data type(collection).
*
* @author 20162330
*/
public class Bag<T> implements BagInterface<T> {
private Object food[] = new Object[5];
/*
Returns the current number of objects in the bag(except null).
*/
public int getCurrentSize() {
int foodSize = 0;
for (Object i : food) { //foreach遍歷
if (i != null)
foodSize++;
}
return foodSize;
}
/*
Demonstrates if the food bag is empty(null).
*/
public boolean isEmpty() {
boolean boo = true; //默認(rèn)為空
for (Object i : food) {
if (i != null) {
boo = false;
break;
}
}
return boo;
}
/*
Adds a given object to the food bag,according to whether the addition succeeds,
return true or false.
*/
public boolean add(T newEntry) {
boolean boo = false;
for (Object i : food) {
if (i == null) { //foreach不能修改數(shù)組元素
for (int j = 0; j < food.length; j++) { //重新使用for循環(huán)賦值
if (food[j] == i) {
food[j] = newEntry; //添加到第一個(gè)空值位置
break;
}
}
boo = true;
break;
}
}
return boo;
}
/*
Removes an unspecified object from the food bag,if possible.
*/
public T remove() {
Object n = null;
for (int i = 0; i < food.length; i++) {
if (food[i] != null) {
n = food[i];
food[i] = null; //移除第一個(gè)不為空的元素
break;
}
}
return (T) n;
}
/*
Removes an occurrence of a particular object from the food bag,if possible.
*/
public boolean remove(T anEntry) {
boolean boo = false;
int i = 0;
while (i < food.length) {
if (food[i] == anEntry) {
food[i] = null;
boo = true;
break;
}
i++;
}
return boo;
}
/*
Removes all objects from the food bag.
*/
public void clear() {
int i = 0;
do { //do-while方式遍歷
food[i] = null;
i++;
}
while (i < food.length);
}
/*
Counts the number of times an object occurs in the food bag.
*/
public int getFrequencyOf(T anEntry) {
int t = 0;
for (Object i : food) {
if (i == anEntry)
t++;
}
return t;
}
/*
Tests whether the food bag contains a particular object.
*/
public boolean contains(T anEntry) {
boolean boo = false;
for (Object i : food) {
if (i == anEntry) {
boo = true;
break;
}
}
return boo;
}
/*
Shows all objects in a new array of food bag entries.
*/
public T[] toArray() {
int j = food.length - 1;
for (int i = 0;i<food.length;i++) {
food[j] = "apple";
if (food[i] == null) { //將空值全部填補(bǔ)
food[i] = food[j];
j--;
}
}
return (T[]) food;
}
}
感想
- 這次實(shí)踐中解決問(wèn)題確實(shí)花費(fèi)了不少時(shí)間,不過(guò)我一直堅(jiān)持獨(dú)立思考,最終順利解決問(wèn)題,同時(shí)又練習(xí)了一下幾種不同的循環(huán)(遍歷)方式,Junit測(cè)試與之前相比也更全面了,也算是又體驗(yàn)了一回“做中學(xué)”。





浙公網(wǎng)安備 33010602011771號(hào)