2014-06-24 30 views
13

Scala's Try rất hữu ích. Tôi muốn sử dụng mẫu đó, nhưng đăng nhập tất cả các ngoại lệ. Tôi có thể làm cái này như thế nào?Scala - Thử đăng xuất ngoại lệ

+0

Tôi sẽ quay lại và viết câu trả lời chính thức sau. Tuy nhiên, đây là một bài đăng trên blog mà tôi vừa hoàn thành giải quyết vấn đề này (và những người khác có liên quan): http://fromjavatoscala.blogspot.com/2016/09/rethinking-scalautiltry.html – chaotic3quilibrium

Trả lời

19

Xác định helper sau:

import scala.util.{Try, Failure} 

def LogTry[A](computation: => A): Try[A] = { 
    Try(computation) recoverWith { 
    case e: Throwable => 
     log(e) 
     new Failure(e) 
    } 
} 

Sau đó, bạn có thể sử dụng nó như là bạn sẽ sử dụng Try, nhưng bất kỳ ngoại lệ sẽ được đăng nhập thông qua log(e).

+1

Sẽ thử. BTW, để phù hợp Hãy thử mã nguồn https://github.com/scala/scala/blob/v2.11.1/src/library/scala/util/Try.scala#L192, không nên chỉ bắt các ngoại lệ phi NonFatal? – SRobertJames

+0

@SRobertJames - Không, bởi vì nó đã chỉ bắt được ngoại lệ NonFatal, do đó, chỉ có những người để phục hồi từ. Trừ khi bạn có nghĩa là: tôi có thể đăng nhập tất cả các ngoại lệ từ Hãy thử _bao gồm những ngoại lệ mà nó không thể bắt_không? Thế thì câu trả lời là không; bạn phải chèn mã đăng nhập tùy chỉnh của riêng mình. –

+0

Giải pháp này sẽ không phát ra quá trình ghi nếu 'Throwable' là! NonFatal. – chaotic3quilibrium

5

Bạn có thể tinh chỉnh nó thậm chí còn tiếp tục sử dụng ngầm lớp

def someMethod[A](f: => A): Try[A] = Try(f) 

implicit class LogTry[A](res: Try[A]) { 
    def log() = res match { 
    case Success(s) => println("Success :) " + s); res 
    case Failure(f) => println("Failure :(" + f); res 
    } 
} 

Bây giờ bạn có thể gọi someMethod và theo yêu cầu kết quả của nó log như thế này:

scala> someMethod(1/0).log 
Failure :(java.lang.ArithmeticException:/by zero 

scala> someMethod(1).log 
Success :) 1 

Trong số khóa học println phương pháp bên trong tiềm ẩn clas s có thể được thay thế bằng bất kỳ đăng nhập nào bạn muốn.

+0

Bạn có thể không muốn có tham số kiểu 'A' trên' log', vì nó đổ bóng 'A' của' LogTry'. –

+0

Bạn nói đúng. Sửa chữa nó ngay bây giờ. – goral

+1

Bạn cũng có thể trả về 'res' thay vì trả về' Success (s) 'và' Failure (f) ', tạo ra các cá thể mới. – Kigyo

1

Bạn đã sử dụng thuật ngữ "ngoại lệ" không rõ ràng. (java.lang.)Throwable là gốc của bất kỳ thứ gì có thể được đặt sau cụm từ throw. java.lang.Exception là một trong hai hậu duệ của Throwable (số khác là java.lang.Error). Hơn nữa làm cho điều này mơ hồ là java.lang.RuntimeException, hậu duệ của Exception, có thể là nơi bạn chủ yếu muốn dành thời gian đăng nhập của mình (trừ khi bạn đang thực hiện khung ứng dụng cấp thấp hơn hoặc triển khai trình điều khiển phần cứng).

Giả sử bạn đang muốn đăng nhập theo nghĩa đen TẤT CẢ các trường hợp của Throwable, sau đó bạn sẽ cần một cái gì đó như thế này (không khuyến khích):

def logAtThrowable(f: => A): Try[A] = 
    try 
    Try(f) match { 
     case failure @ Failure(throwable) => 
     log(s"Failure: {throwable.getMessage}") 
     failure 
     case success @ _ => 
     //uncomment out the next line if you want to also log Success-es 
     //log(s"Success: {throwable.getMessage}") 
     success 
    } 
    catch throwable: Throwable => { 
    //!NonFatal pathway 
    log(s"Failure: {throwable.getMessage}") 
    throw throwable 
    } 

Các bên ngoài try/catch là cần thiết để nắm bắt tất cả các Throwable trường hợp đó được lọc đi bởi scala.util.control.NonFatal trong khối try/catch của Try.

Điều đó nói rằng ... có quy tắc Java/JVM: bạn nên never define a catch clause at the resolution of Throwable (một lần nữa, trừ khi bạn đang thực hiện khung ứng dụng cấp thấp hơn hoặc triển khai trình điều khiển phần cứng).

Theo ý định của quy tắc này, bạn sẽ cần thu hẹp số Throwable để bạn chỉ ghi nhật ký phát ra ở cấp độ hạt mịn hơn, nói điều gì đó tinh tế hơn, như java.lang.RuntimeException. Nếu vậy, các mã sẽ trông như thế này (đề nghị):

def logAtRuntimeException(f: => A): Try[A] = 
    Try(f) match { 
    case failure @ Failure(throwable) => 
     throwable match { 
     case runtimeException: RuntimeException => 
      log(s"Failure: {runtimeException.getMessage}") 
     } 
     failure 
    case success @ _ => 
     success 
    } 

Trong cả hai đoạn mã trên, bạn sẽ nhận thấy rằng tôi đã sử dụng match như trái ngược với .recoverWith. Điều này nhằm tạo điều kiện dễ dàng khi thêm lại throw hoạt động. Nó chỉ ra rằng tất cả các phương pháp trên Try là chính họ cũng được bao bọc với các khối try/catch. Điều này có nghĩa rằng nếu bạn muốn đăng nhập những Throwable và sau đó tái throw nó, nếu bạn đang sử dụng một trong những Try phương pháp như recoverWith, tái throw là ngay lập tức recaught và đặt vào một Failure do đó hoàn toàn phá hoại giá trị của việc tái cố ý throw.Bằng cách sử dụng match, lại throw được đảm bảo thành công vì vẫn còn bên ngoài bất kỳ phương thức nào trong số Try.

Nếu bạn muốn xem thêm các lỗ thỏ xung quanh khu vực cụ thể này, tôi đã tạo một blog post of my own exploration.

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