i++引起的慘案

導語

最近在作一個項目,該項目原來是由C++編寫的,如今須要翻譯成Java,在翻譯過程當中,同事沒有理解清楚具體需求,在開發中進行了直譯,直到生產上出現故障了(測試竟然沒測出來?)一塊兒研究才發現...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);
			}
		}
	}
複製代碼

而後看一下同事翻譯的初版的樣子spa

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條件以後便可,可是分組代碼真的要寫這麼晦澀嗎?見仁見智了...code

總結

不多有人真的去注意for循環條件執行的順序,以及i++和++i的區別,我老大在看到這段代碼的時候也猶豫了一下,後來才恍然大悟懷疑這個i++在break以後是否有執行,測試了一下才肯定本身的想法。平時這種代碼可能都是不會有問題的,可是一旦觸發了某種邊界條件,就不必定是你想象的樣子了。因此在寫代碼以前,仍是要明確本身要實現的功能是什麼,若是同事最開始翻譯代碼的時候明確知道這是一段分組代碼,我相信他絕對不會這樣去寫的!開發

相關文章
相關標籤/搜索