Với một Option, cách thành ngữ để nhận được giá trị của nó hoặc cố gắng ném một ngoại lệ là gì?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
Với một Option, cách thành ngữ để nhận được giá trị của nó hoặc cố gắng ném một ngoại lệ là gì?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
NoSuchElementException
. Ngoài ra, hầu hết thời gian phương pháp này có thể trả về Không một cách an toàn.
Câu trả lời:
(CHỈNH SỬA: đây không phải là cách tốt nhất hoặc thành ngữ nhất để làm điều đó. Tôi đã viết nó khi tôi chưa quen với Scala. Tôi để nó ở đây để làm ví dụ về cách không làm điều đó. Ngày nay tôi sẽ làm với tên @TravisBrown)
Tôi nghĩ rằng nó thực sự tóm gọn lại hai điều:
Nếu tại thời điểm đó trong mã của bạn, bạn mong đợi giá trị ở đó và trong trường hợp từ xa, đó không phải là bạn muốn chương trình của mình bị lỗi nhanh, thì tôi sẽ chỉ làm bình thường get
và để Scala ném NoSuchElementException
nếu không có giá trị :
def foo (): String = { val x: Tùy chọn [Chuỗi] = ... x.get }
Nếu bạn muốn xử lý trường hợp theo cách khác (hãy đưa ra ngoại lệ của riêng bạn), tôi nghĩ một cách thanh lịch hơn sẽ trông như thế này:
def foo (): String = { val x: Option [String] = Không có x khớp với { trường hợp Some (giá trị) => giá trị case None => ném MyRuntimeException mới ("blah") } }
Và tất nhiên nếu bạn muốn cung cấp giá trị thay thế của riêng bạn cho các trường hợp đó Option
là None
bạn sẽ chỉ cần sử dụng getOrElse
:
def foo (): String = { val x: Option [String] = Không có x.getOrElse ("giá trị thay thế của tôi") }
getOrElse
sẽ được ưu tiên nếu đó là tất cả những gì người ta muốn làm. Tôi chỉ muốn minh họa trường hợp "nếu được xác định, đánh giá thành X, nếu không, ném ngoại lệ".
getOrElse
như vậy:x.getOrElse(throw new MyRuntimeException("message"))
Một throw
"câu lệnh" thực sự là một biểu thức trong Scala, và nó có kiểu Nothing
, là kiểu con của mọi kiểu khác. Điều này có nghĩa là bạn chỉ có thể sử dụng cũ đơn giản getOrElse
:
def myGet[A](oa: Option[A]) = oa.getOrElse(throw new RuntimeException("Can't."))
Tuy nhiên, bạn thực sự không nên làm điều này.
OutOfMemoryError
).
Chỉ cần sử dụng phương thức .get.
def get[T](o:Option[T]) = o.get
Nó sẽ ném ra một NoSuchElementException nếu o là một thể hiện của None.
Về cơ bản, tôi sẽ làm việc với các tùy chọn như sau:
def addPrint(oi:Option[Int]) = oi.map(_+1).foreach(println)
addPrint(Some(41))
addPrint(Some(1336))
addPrint(None)
để tránh câu hỏi cụ thể của bạn.
get
là một ý kiến tồi — nó chỉ phản ánh thực tế là bạn đang làm điều gì đó khó chịu. Việc sử dụng getOrElse
và ném một cách rõ ràng một ngoại lệ sẽ khó bỏ lỡ hơn khi bạn quyết định đây không chỉ là mã một lần và muốn làm cho nó an toàn hơn.
Tôi hy vọng điều này sẽ giúp bạn hiểu cách biểu diễn lỗi (và nói chung là hiệu ứng) bằng cách sử dụng các loại.
Sử dụng Option
để trả về các giá trị tùy chọn. Ví dụ - không tìm thấy thực thể trong bộ nhớ.
Sử dụng Option(possiblyNull)
để tránh các trường hợp Some(null)
.
Sử dụng Either[Error, T]
để báo cáo lỗi dự kiến. Ví dụ - định dạng email sai, không thể phân tích cú pháp chuỗi thành số, v.v.
Lập mô hình lỗi của bạn dưới dạng ADT (nói đơn giản là loại cấu trúc phân cấp loại) để sử dụng nó, ví dụ, ở Bên trái của Một trong hai để biểu diễn các tình huống lỗi phức tạp hơn.
Ném Exception
chỉ để báo hiệu những hỏng hóc không mong muốn và không thể khôi phục. Như thiếu tệp cấu hình.
Sử dụng Either.catchOnly
hoặc Try
hoặc Cats.IO
(nâng cao) thay vì một khối bắt để xử lý các lỗi không mong muốn. Gợi ý: Bạn vẫn có thể sử dụng ADT nhưng mở rộng chúng từ các vật có thể ném. Thông tin thêm về Either
vsTry
.
Sử dụng kiểu Validated
dữ liệu từ Cats lib để tích lũy lỗi thay vì fail-fast ( Either
), nhưng thích Either ở cấp mô-đun để đơn giản hóa thành phần của chương trình (để có các kiểu giống nhau). Ví dụ - xác thực dữ liệu biểu mẫu, tích lũy lỗi phân tích cú pháp.
Sử dụng các loại đã đề cập và không tối ưu hóa chương trình trước - vì hầu hết có thể, các kiểu cổ chai sẽ nằm trong logic kinh doanh, không phải trong các loại hiệu ứng.
Cách tiếp cận như vậy sẽ đơn giản hóa việc bảo trì và cập nhật mã của bạn vì bạn có thể lý luận về nó mà không cần đi đến chi tiết triển khai cụ thể (hay còn gọi là lý luận cục bộ). Ngoài ra - giảm lỗi - bạn không thể bỏ lỡ một lỗi trong loại. Và soạn chương trình dễ dàng hơn (với sự giúp đỡ của map
, flatMap
và combinators khác) - vì nó đơn giản về mức loại, chứ không phải là trường hợp ngoại lệ với phi địa phương và tác dụng phụ.
Tìm hiểu thêm về chức năng Scala.
Nhưng hãy lưu ý rằng đôi khi với cách tiếp cận này, các kiểu tiếp cận có thể chồng chất lên nhau và việc soạn thảo mọi thứ có thể trở nên khó khăn hơn. Ví dụ: x: Future[Either[Error, Option[T]]]
Bạn có thể làm gì:
map
và flatMap
kết hợp với so khớp mẫu để tạo các giá trị khác nhau của các loại như vậy, ví dụ:x.faltMap { case Right(Some(v)) => anotherFuture(v); case Left(er) => ... }
Future[Option[T]]
Và cuối cùng, trong trường hợp của bạn, một lựa chọn sẽ là:
def foo() : Either[Error, String] = {
val x : Option[String] = ...
x match {
case Some(v) => Right(v)
case None => Left(Error(reason))
}
}
Scala hiện hỗ trợ thao tác này trên bản đồ bằng getOrElse()
phương pháp, xem tài liệu tại đây
Như đã chỉ ra, ném một ngoại lệ trong Scala cũng là một biểu thức.
Vì vậy, bạn có thể làm như sau:
myMap.getOrElse(myKey, throw new MyCustomException("Custom Message HERE")
.get
?