導語
最近在做一個專案,該專案原來是由C++編寫的,現在需要翻譯成Java,在翻譯過程中,同事沒有理解清楚具體需求,在開發中進行了直譯,直到生產上出現故障了(測試居然沒測出來?)一起研究才發現...
需求
先看一下C++程式碼的樣子
//vector<CorBillGroup>& vCorBillGroups
while(rs->next()){
if(load(rs,bill)){
//判斷分組
corId = pm->getCorpId(bill.getPartyRoleId(),bill.getBillingCycleId());
if (corId < 0)
throw BusinessLogicException("找不到銀行劃扣分組ID", corId);
int i=0;
for ( i=0;i<vCorBillGroups.size();i++) {
if(vCorBillGroups[i].getCorperationId() == corId){
vCorBillGroups[i].add(bill);
break;
}
}
if(i == vCorBillGroups.size()) {
CorBillGroup corBillGroup;
corBillGroup.setCorperationId(corId);
corBillGroup.add(bill);
vCorBillGroups.push_back(corBillGroup);
}
}
}
複製程式碼
然後看一下同事翻譯的第一版的樣子
for (VirtualBillVO vo : bills) {
long partyRoleId = vo.getPartyRoleId();
Partner partner = getPartnerManager().getPartner(Integer.parseInt(partyRoleId + ""));
corId = partner.getCorpId();
if (corId < 0) {
throw new BusinessLogicException("找不到銀行劃扣分組ID", corId);
}
int i = 0;
for (CorBillGroupVO corVo : vCorBillGroups) {
i++;
if (corVo.getM_iCorperationId() == corId) {
corVo.add(vo);
break;
}
}
if (i == vCorBillGroups.size()) {
CorBillGroupVO corBillGroup = new CorBillGroupVO();
corBillGroup.setM_iCorperationId(corId);
corBillGroup.add(vo);
vCorBillGroups.add(corBillGroup);
}
}
複製程式碼
分析
先不考慮這裡具體是幹啥的,單純從程式碼結構的角度來看,似乎程式碼是沒啥問題的。但是程式可不是看程式碼結構的! 由於C++程式碼有具體業務含義,簡單介紹一下這段程式碼的背景:給一組賬單(bills),按照運營商(corId)進行分組後返回。這麼簡單的需求,也不知道C++程式碼為啥寫的這麼複雜,這也導致翻譯這段程式碼的同事犯了糊塗。
- 具體看C++: 迴圈賬單列表,取賬單的corId,i=0.迴圈分組vCorBillGroups,若分組裡有該corId就將該賬單新增到vCorBillGroups的第i個元素中,並跳出迴圈,判斷i 和vCorBillGroups長度是否相等,若在前面迴圈中有break,i是不可能與vCorBillGroups的長度相等的(迴圈vCorBillGroups.size()次,每次i++,則當迴圈正常結束後,i肯定與vCorBillGroups.size()相等,若有break,則i肯定小於vCorBillGroups.size()了);若前面迴圈中沒有break,說明該corId不在已經存在的組中,則新增到vCorBillGroups中。
- 再看同事翻譯的Java:同樣迴圈賬單列表,但是採用了foreach的寫法遍歷,break條件滿足之前就i++,那麼在無break的時候是正常的,在有break的情況下就不一樣了,例如vCorBillGroups.size() = 1 時,遍歷一次vCorBillGroups,此時就算有break,i = 1,i ==vCorBillGroups.size();又會重新新增一次分組,那麼就與C++程式碼的含義不一樣了。
- 區別在哪裡呢,仔細對比一下就會發現,兩處程式碼的區別就在i++的使用上,for(;;i++)中,若for迴圈中有break,是不會進行++操作的,同事翻譯時為了圖方便,隨意使用foreach,並將i++置於break條件之前,很明顯無法起到控制作用了!
解決
最簡單的解決方式就是如上述分析一樣,將i++放在break條件之後即可,但是分組程式碼真的要寫這麼晦澀嗎?見仁見智了...
總結
很少有人真的去注意for迴圈條件執行的順序,以及i++和++i的區別,我老大在看到這段程式碼的時候也猶豫了一下,後來才恍然大悟懷疑這個i++在break之後是否有執行,測試了一下才確定自己的想法。平時這種程式碼可能都是不會有問題的,但是一旦觸發了某種邊界條件,就不一定是你想象的樣子了。所以在寫程式碼之前,還是要明確自己要實現的功能是什麼,如果同事最開始翻譯程式碼的時候明確知道這是一段分組程式碼,我相信他絕對不會這樣去寫的!