August 09 23:08 ~ August 10 00:06
前幾天學弟問了 Teddy 幾個 exception handling 的問題,在回答問題的過程中,Teddy 突然想到一個問題:
為什麼 Java 與 C# 都建議在 finally clause 中不要丟出 exception?
路人甲內心獨白:蝦米,有這款ㄟ代誌,奈嚨沒人通知我?
請鄉民們先花三分鐘想一下這個問題再繼續看下去。
喂,還沒三分鐘不要偷看。
請參考以下從 Java Programming Language, 4th 這本書所節錄的程式片段。
先複習一下 Java 的 try block 用法,一個 try block 包含三個結構:
- try clause: 放正常程式碼的地方,這裡面的程式碼有可能會丟出 exceptions,如果想要處理例外情況,則需要用 try clause 包起然後在 catch clause 中捕捉與進一步處理。
- catch clause: 捕捉與處理例外的地方,也就是一般所謂的 exception handler (例外處理程式) 就是寫在這裡。實務上,大部分的人都不知道例外要如何處理,所以常常會發現 catch clause 的實做出現所謂的 dummy handler 這個 bad smell (參考 這裡)。另一種常見的處理方法就是將捕捉到的例外轉成另一個型態的例外,然後回報給 caller。這種作法所丟出的例外通常屬於 failure exception,也就是說這個例外用以表示該 method 的執行是失敗的。當 caller 收到一個 failure exception 時,可以依據實際情況進一步決定要如何處理(路人甲:這句有講等於沒講)。嗯嗯,請不懂的鄉民們去拜讀一下 這裡面的文章。
- finally clause: 不論 try clause 的執行是否會丟出例外,finally clause 裡面的程式碼一定都會被執行。這裡面通常用來放 cleanup 的程式碼(釋放資源以免發生 memory leak)。
現在假設你寫了一個類似上面 getDataSet () 的程式碼,但是你在 finally clause 裡面(第13 行) 丟出了一個 CleanupException 用以表示釋放資源失敗。
throw new CleanupException();
現在假裝你是使用 getDataSet () 的人,所以你有可能會收到兩個例外,這兩個例外的『
語意』分別表示。
- BadDataSetException: 表示執行 getDataSet () 失敗。
- CleanupException: 表示 getDataSet () 釋放資源失敗。
但是問題來了,如果這兩個例外先後發生那該怎麼辦?也就是說,程式執行到第5行發生了 IOException,然後控制權跳到第7行,接著執行到第8行丟出BadDataSetException,然後控制權跳到第9行的 finally clause。不幸地,第11行的 in.close() 又丟出 IOException,然後控制權就跳到13行。現在的13行已經被改成剛剛的 throw new CleanupException(); 這個敘述,所以原本的 BadDataSetException 被『
蓋台』了,最後被丟出去的變成 CleanupException。
也就是說,如果 finally clause 中也丟出例外,那麼當 caller 收到 CleanupException 時,這個 CleanupException 的語意是不清楚的。
為什麼?講到這裡還不知道... 因為此時 CleanupException 可能代表:
- 單純的釋放資源失敗,或是。
- 執行 getDataSet () 失敗加上釋放資源失敗 。
***
至於解法呢?嗯嗯,這個案子目前已經進入司法階段,不便對外說明(眾鄉民們大聲吶喊:真欠扁)。話說 Teddy 當年為了解決這個問題還寫了一篇 journal paper,不過後來 paper 被要求要 revise, 但是當時 Teddy 快畢業而且點數也收集夠了,就偷懶沒改,放到現在 paper 都快『生菇 (發霉)』了。算一算當年為了寫這篇 paper 花了應該有幾百個小時在這個問題上面,還拖累 Teddy 的指導教授一起受苦。這算是某種『無病呻吟』的研究題目嗎...XD。
***
友藏內心獨白:就算違反這個規則大家還不是活的好好的,code 照跑,舞照跳。