2012-01-14 26 views
20

Có cách nào tốt hơn để đảm bảo tài nguyên được phát hành đúng cách hay không - cách tốt hơn để viết mã sau đây?Scala cuối cùng chặn tài nguyên đóng/bẻ khóa

 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

Bởi vì tôi cần để có thể tổ nhiều trường java.lang.AutoCloseable, mỗi trong số đó phụ thuộc vào người trước đó đã thành công ngay lập tức, cuối cùng tôi đã đạt được một mô hình rất hữu ích đối với tôi. Tôi đã viết nó như là một câu trả lời cho câu hỏi tương tự StackOverflow: http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium

Trả lời

18

Something như đó là một ý tưởng tốt, nhưng tôi muốn làm cho nó một phương pháp:

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 
    } 
} 

(lưu ý rằng chúng tôi chỉ bắt một lần, nếu bạn thực sự muốn có một thông điệp được in trong một trường hợp và không khác, sau đó bạn phải bắt cả hai như bạn đã làm). (Cũng lưu ý rằng tôi chỉ bắt ngoại lệ; bắt Error cũng thường là không khôn ngoan, vì nó hầu như không thể phục hồi từ). Phương pháp này được sử dụng như sau:

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

Kể từ khi nó trả về một giá trị, bạn sẽ nhận được một Some(()) nếu thành công ở đây (bạn có thể bỏ qua).


Chỉnh sửa: để làm cho tổng quát hơn, tôi thực sự sẽ trả lại số Either thay vào đó, vì vậy bạn sẽ có ngoại lệ. Giống như vậy:

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) } 
} 

Bây giờ nếu bạn nhận được Right, tất cả đều ổn. Nếu bạn nhận được Left, bạn có thể chọn ngoại lệ của mình. Nếu bạn không quan tâm đến ngoại lệ, bạn có thể sử dụng .right.toOption để ánh xạ nó thành một tùy chọn hoặc chỉ sử dụng .right.map hoặc bất kỳ điều gì để hoạt động trên kết quả chính xác chỉ khi nó ở đó (giống như với Option). (Kết hợp mẫu là một cách hữu ích để xử lý Either s.)

+0

Tại sao bạn chỉ định tài nguyên cho r? Tại sao không sử dụng nó trực tiếp. Bạn có thấy điều gì sai trái với việc đơn giản hóa này không, hãy xóa một lần thử. 'def cleanly [A <: java.io.Có thể đóng, B] (tài nguyên: A) (mã: A => B): Hoặc [Ngoại lệ, B] = { thử { Phải (mã (tài nguyên)) } bắt { trường hợp e: Ngoại lệ => Trái (e) } cuối cùng { tài nguyên.close } } ' – rvange

+3

@rvange - Tạo tài nguyên có thể gây ra ngoại lệ, do đó bạn muốn gọi theo tên. Tài nguyên có thể không phải là 'java.io.Closeable', do đó, nó chung chung hơn để cho phép dọn dẹp do người dùng chỉ định. Nếu bạn chỉ có 'java.io.Closeable' và bạn chắc chắn rằng tài nguyên sẽ tự tạo ra mà không có ngoại lệ, hoặc bạn muốn ngoại lệ đó lan truyền, thì mã của bạn là tốt. –

17

Có một cái nhìn tại Scala-ARM

Dự án này nhằm mục đích là dự án Scala vườn ươm cho Automatic-Resource Management-trong thư viện scala ...

... Các Scala ARM thư viện cho phép người dùng đảm bảo việc đóng tài nguyên trong các khối mã bằng phương thức "được quản lý". Phương thức "được quản lý" về cơ bản có một đối số "bất kỳ thứ gì có phương thức đóng hoặc hủy bỏ" và xây dựng một đối tượng ManagedResource mới.

+0

Bạn có biết tình trạng của Scala-ARM không? Nó trông khá chết - không cam kết kể từ tháng Năm. –

0

Hoặc bạn có thể làm điều này với mẫu đơn Choppy's Lazy TryClose.

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 

Thông tin thêm ở đây: https://github.com/choppythelumberjack/tryclose

(chỉ cần chắc chắn để nhập khẩu tryclose._tryclose.JavaImplicits._)

Các vấn đề liên quan