2011-06-20 22 views
31

Trong ngôn ngữ Groovy, nó rất đơn giản để kiểm tra null hoặc false như:Làm thế nào để kiểm tra null hoặc sai trong Scala chính xác?

đang groovy:

def some = getSomething() 
if(some) { 
// do something with some as it is not null or emtpy 

} 

Trong Groovy nếu somenull hoặc là chuỗi rỗng hoặc là zero số vv sẽ đánh giá đến false. Phương pháp thử nghiệm ngắn gọn tương tự cho null hoặc false trong Scala là gì? Câu trả lời đơn giản cho phần này của câu hỏi giả định some đơn giản chỉ là kiểu chuỗi Java?

Ngoài ra một phương pháp tốt hơn trong groovy là:

def str = some?.toString() 

có nghĩa là nếu some không phải là null sau đó phương pháp toString trên some sẽ được gọi thay vì ném NPE trong trường hợp somenull. Tương tự như thế nào ở Scala?

+0

IMO, câu trả lời của Daniel dưới đây là phù hợp nhất. Scala đã có rất nhiều hỗ trợ để xử lý các giá trị có khả năng trống - tốt hơn là tìm hiểu và sử dụng nó hơn là tạo một cú pháp '?' Mới với hàm ý. – Bill

+1

Một lý do để đi với 'null' thay vì' None 'có thể là hiệu suất/sử dụng bộ nhớ - bạn có thể thích phân bổ một đối tượng ngắn ngủi từ chuyển đổi ngầm trên bay thay vì giữ một trình bao bọc' Option'. Cấp, không nhiều người viết mã hiệu năng cao trên JVM, nhưng tôi vẫn nói nó có các ca sử dụng của nó. – axel22

Trả lời

49

Điều bạn có thể bị thiếu là một hàm như getSomething trong Scala có thể sẽ không trả về null, chuỗi rỗng hoặc số không. Một hàm có thể trả về một giá trị có ý nghĩa hoặc có thể không có giá trị trả về là Option - nó sẽ trả lại Some(meaningfulvalue) hoặc None.

Sau đó bạn có thể kiểm tra này và xử lý các giá trị có ý nghĩa với một cái gì đó giống như

val some = getSomething() 
some match { 
    case Some(theValue) => doSomethingWith(theValue) 
    case None   => println("Whoops, didn't get anything useful back") 
} 

Vì vậy, thay vì cố gắng để mã hóa các "thất bại" giá trị trong giá trị trả về, Scala đã hỗ trợ cụ thể cho các "trở lại chung một cái gì đó có ý nghĩa hoặc chỉ ra thất bại "trường hợp.

Có nói rằng, Scala tương thích với Java và Java trả về null từ các hàm mọi lúc. Nếu getSomething là một hàm Java trả về null, có một đối tượng nhà máy sẽ làm cho một số hoặc không có giá trị trả về.

Vì vậy

val some = Option(getSomething()) 
    some match { 
    case Some(theValue) => doSomethingWith(theValue) 
    case None   => println("Whoops, didn't get anything useful back") 
    } 

... mà là khá đơn giản, tôi tuyên bố, và sẽ không đi NPE vào bạn.

Các câu trả lời khác đang làm những điều thú vị và thành ngữ, nhưng điều đó có thể nhiều hơn mức bạn cần ngay bây giờ.

+0

Cảm ơn! Tôi đã kết thúc bằng cách sử dụng giải pháp của bạn nhưng xin lỗi tôi đã đánh dấu một câu trả lời là giải pháp. – ace

+1

đây là tính năng scala tồi tệ nhất mà tôi gặp phải trong hành trình một tháng của tôi .. đó là điều đáng tiếc đối với hệ tư tưởng "kiểu ít" của Scala. imo NPE dễ sửa chữa và bạn không thể dựa vào lang để làm mọi thứ cho bạn. Ngay cả với Tùy chọn mọi người có thể viết mã sẽ dẫn đến NPE. tức là nếu thay vì trường hợp trùng khớp nếu ai đó chỉ làm một số (val) .get thì bạn vẫn nhận được NPE. có lẽ xuống dòng này có thể trông hữu ích hơn nhưng cho đến nay tôi havent gặp phải bất kỳ bài viết giải thích khác – nir

+0

Tiếp tục đi, và tôi dự đoán bạn sẽ thay đổi tâm trí của bạn. Tất nhiên bạn có thể kích động một NPE nếu bạn cố gắng, nhưng thực hành tốt trong Scala là không sử dụng Option .get - có nhiều cách tốt hơn để làm điều đó. –

19

Trong Scala, các biểu thức bạn mô tả có nghĩa là một phương pháp gọi là ? được gọi trên một đối tượng có tên là some. Thường xuyên, các đối tượng không có phương thức được gọi là ?. Bạn có thể tạo chuyển đổi tiềm ẩn của riêng mình thành đối tượng có phương thức ? để kiểm tra null ness.

implicit def conversion(x: AnyRef) = new { 
    def ? = x ne null 
} 

Ý chí trên, trong bản chất, chuyển đổi bất kỳ đối tượng mà bạn gọi phương thức ? vào biểu hiện ở phía bên tay phải của phương pháp conversion (mà không có phương pháp ?). Ví dụ, nếu bạn làm điều này:

"".? 

trình biên dịch sẽ phát hiện rằng một đối tượng String không có phương pháp ?, và viết lại nó vào:

conversion("").? 

Illustrated trong một thông dịch viên (lưu ý rằng bạn có thể bỏ qua . phương pháp khi kêu gọi các đối tượng):

scala> implicit def any2hm(x: AnyRef) = new { 
    | def ? = x ne null 
    | } 
any2hm: (x: AnyRef)java.lang.Object{def ?: Boolean} 

scala> val x: String = "!!" 
x: String = "!!" 

scala> x ? 
res0: Boolean = true 

scala> val y: String = null 
y: String = null 

scala> y ? 
res1: Boolean = false 

Vì vậy, bạn có thể viết:

if (some ?) { 
    // ... 
} 

Hoặc bạn có thể tạo ra một chuyển đổi ngầm vào một đối tượng với một phương pháp ? đó gọi phương thức cụ thể về đối tượng nếu đối số không phải là null - làm điều này:

scala> implicit def any2hm[T <: AnyRef](x: T) = new { 
    | def ?(f: T => Unit) = if (x ne null) f(x) 
    | } 
any2hm: [T <: AnyRef](x: T)java.lang.Object{def ?(f: (T) => Unit): Unit} 

scala> x ? { println } 
!! 

scala> y ? { println } 

để bạn có thể sau đó viết:

some ? { _.toString } 

Building (đệ quy) về câu trả lời soc, bạn có thể mô hình phù hợp trên x trong các ví dụ trên để tinh chỉnh những gì ? d oes tùy thuộc vào loại x. : D

+0

điều này thật tuyệt! nếu bạn có thể giải thích toán tử 'eq' và' ne' tạo thành ý chính của câu trả lời này, điều đó sẽ hữu ích hơn nữa – asgs

+1

https://stackoverflow.com/questions/10066927/what-is-the-difference-between- a-nenull-and-a-null-in-scala – axel22

6

Bạn có thể tự viết một số trình bao bọc hoặc sử dụng loại Tùy chọn.

Tôi thực sự sẽ không kiểm tra null. Nếu có một số null ở đâu đó, bạn nên sửa chữa và không xây dựng các kiểm tra xung quanh nó.

xây dựng trên đầu trang của câu trả lời axel22 của:

implicit def any2hm(x: Any) = new { 
    def ? = x match { 
    case null => false 
    case false => false 
    case 0 => false 
    case s: String if s.isEmpty => false 
    case _ => true 
    } 
} 

Chỉnh sửa: Điều này dường như sụp đổ một trong hai trình biên dịch hoặc không hoạt động. Tôi sẽ điều tra.

+1

Vấn đề là để khai báo 'x' là' Any', nó đưa ra 2 chuyển đổi ngầm cho phương thức '?' vào phạm vi. – axel22

+0

Điều gì sẽ xảy ra nếu một số thuộc loại Java String? Có vẻ như scala thiếu tính conciseness của groovy. – ace

+0

Tôi xin lỗi nhưng tôi không theo cả hai câu trả lời nhưng cảm ơn vì đã cố gắng. Tôi chỉ tìm kiếm giải pháp đơn giản. – ace

33

Vâng, Boolean không được là null, trừ khi được chuyển thành thông số loại. Cách để xử lý null là để chuyển đổi nó thành một Option, và sau đó sử dụng tất cả các công cụ Option. Ví dụ:

Option(some) foreach { s => println(s) } 
Option(some) getOrElse defaultValue 

Kể từ Scala là tĩnh gõ, một điều không thể là "một null hoặc là chuỗi rỗng hoặc là zero số vv". Bạn có thể vượt qua một Any có thể là bất kỳ thứ gì trong số đó, nhưng sau đó bạn phải khớp với từng loại để có thể làm bất kỳ điều gì hữu ích với nó. Nếu bạn thấy mình trong tình huống này, bạn rất có thể không làm Scala thành ngữ.

6

Nếu bạn sử dụng extempore's null-safe coalescing operator, sau đó bạn có thể viết str ví dụ của bạn như

val str = ?:(some)(_.toString)() 

Nó cũng cho phép bạn chuỗi mà không lo lắng về null s (do đó "coalescing"):

val c = ?:(some)(_.toString)(_.length)() 

Trong số Tất nhiên, câu trả lời này chỉ đề cập đến phần thứ hai của câu hỏi của bạn.

6

Những gì bạn yêu cầu là một cái gì đó trong dòng Safe Navigation Operator (?.) của Groovy, andand gem của Ruby hoặc accessor variant of the existential operator (?.) của CoffeeScript.Đối với trường hợp như vậy, tôi thường sử dụng ? phương pháp RichOption[T] tôi, được định nghĩa như sau

class RichOption[T](option: Option[T]) { 
    def ?[V](f: T => Option[V]): Option[V] = option match { 
    case Some(v) => f(v) 
    case _ => None 
    } 
} 

implicit def option2RichOption[T](option: Option[T]): RichOption[T] = 
    new RichOption[T](option) 

và sử dụng như sau

scala> val xs = None 
xs: None.type = None 

scala> xs.?(_ => Option("gotcha")) 
res1: Option[java.lang.String] = None 

scala> val ys = Some(1) 
ys: Some[Int] = Some(1) 

scala> ys.?(x => Some(x * 2)) 
res2: Option[Int] = Some(2) 
0

Sử dụng mô hình kết hợp như đề xuất trong một vài câu trả lời ở đây là một đẹp cách tiếp cận:

val some = Option(getSomething()) 
    some match { 
    case Some(theValue) => doSomethingWith(theValue) 
    case None   => println("Whoops, didn't get anything useful back") 
    } 

Tuy nhiên, một chút tiết.

Tôi thích map một Option theo cách sau:

Option(getSomething()) map (something -> doSomethingWith(something))

Một lót, ngắn, rõ ràng.

Lý do là tùy chọn có thể được xem là some kind of collection - một số bông tuyết đặc biệt của bộ sưu tập chứa phần tử bằng 0 hoặc chính xác một phần tử của loại và như bạn có thể ánh xạ Danh sách [A] thành Danh sách [B ], bạn có thể ánh xạ một Option [A] thành Option [B]. Điều này có nghĩa là nếu thể hiện Tùy chọn [A] của bạn được xác định, tức là nó là Một số [A], kết quả là Một số [B], nếu không thì đó là Không. Nó thực sự mạnh mẽ!

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