2012-01-14 49 views
20

有沒有更好的方法來確保資源正確釋放 - 一種更好的方式來編寫下面的代碼?斯卡拉終於阻止關閉/刷新資源

 val out: Option[FileOutputStream] = try { 
      Option(new FileOutputStream(path)) 
     } catch { 
      case _ => None 
     } 


     if (out.isDefined) { 

      try { 
      Iterator.continually(in.read).takeWhile(-1 != _).foreach(out.get.write) 
      } catch { 
      case e => println(e.getMessage) 
      } finally { 
      in.close 
      out.get.flush() 
      out.get.close() 
      } 

     } 
+0

因爲我需要能夠嵌套多個java.lang.AutoCloseable實例,每個實例的取決於先前的一個成功實例化後,我終於遇到了一個對我來說非常有用的模式。我把它寫成類似StackOverflow問題的答案:http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium 2016-01-22 19:59:48

回答

18

類似的東西是一個好主意,但我會做它的方法:

def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Option[B] = { 
    try { 
    val r = resource 
    try { Some(code(r)) } 
    finally { cleanup(r) } 
    } catch { 
    case e: Exception => None 
    } 
} 

(注意,我們只趕上一次;如果你真的想在一種情況下,而不是打印的消息另一個,那麼你必須像你一樣捕捉到)。 (另請注意,我只捕獲異常;捕獲Error也通常是不明智的,因爲它幾乎不可能從恢復。)該方法用於像這樣:

cleanly(new FileOutputStream(path))(_.close){ fos => 
    Iterator.continually(in.read).takeWhile(_ != -1).foreach(fos.write) 
} 

因爲它返回一個值,你會得到一個Some(())如果它在這裏成功(你可以忽略)。


編輯:以使其更一般的,我真的有它返回一個Either代替,所以你得到的異常。像這樣:

def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Either[Exception,B] = { 
    try { 
    val r = resource 
    try { Right(code(r)) } finally { cleanup(r) } 
    } 
    catch { case e: Exception => Left(e) } 
} 

現在,如果你得到一個Right,一切順利。如果你得到一個Left,你可以挑選你的例外。如果你不關心這個異常,你可以使用.right.toOption將它映射到一個選項中,或者只是使用.right.map或其他任何方法來處理正確的結果,只要它在那裏(就像使用Option一樣)。 (模式匹配是處理Either s的有用方法。)

+0

爲什麼你將資源分配給r?爲什麼不直接使用它。你看到這個簡化有什麼問題,只能嘗試一下。 'def cleanly [A <:java.io.(例如,A => B):或者[例外,B] = {右邊(代碼(資源)) } catch {0122} e) } finally { resource.close } }' – rvange 2013-12-02 23:03:10

+3

@rvange - 生成資源可能會導致異常,所以您想按名稱調用。該資源可能不是'java.io.Closeable',因此允許用戶指定的清理更爲通用。如果你只有'java.io.Closeable',並且你可以肯定資源會毫無例外地自行創建,或者你想讓這個異常傳播,那麼你的代碼就沒問題。 – 2013-12-04 11:46:56

17

看一看Scala-ARM

該項目的目標是在Scala庫自動-資源管理斯卡拉Incubator項目...

...斯卡拉ARM庫允許用戶使用「託管」方法確保在代碼塊內打開關閉資源。 「託管」方法本質上採用「具有close或dispose方法的任何東西」的參數,並構造一個新的ManagedResource對象。

+0

你碰巧知道Scala-ARM的狀態嗎?它看起來已經死了 - 從5月份開始不再提交。 – 2014-10-15 10:29:15

0

或者,您可以使用Choppy的Lazy TryClose monad來執行此操作。

val output = for { 
    fin <- TryClose(in) 
    fout <- TryClose.wrapWithCloser(new FileOutputStream(path))(out => {out.flush(); out.close();}) 
} yield wrap(Iterator.continually(fin.read).takeWhile(-1 != _).foreach(fout.get.write)) 

// Then execute it like this: 
output.resolve 

此處瞭解詳情:https://github.com/choppythelumberjack/tryclose

(只是一定要導入tryclose._tryclose.JavaImplicits._