Điểm của lớp Option [T] là gì?


83

Tôi không thể hiểu được quan điểm của Option[T]đẳng cấp trong Scala. Ý tôi là, tôi không thể nhìn thấy bất kỳ khuyến khích nào Nonekết thúc null.

Ví dụ, hãy xem xét mã:

object Main{
  class Person(name: String, var age: int){
    def display = println(name+" "+age)
  }

  def getPerson1: Person = {
    // returns a Person instance or null
  }

  def getPerson2: Option[Person] = {
    // returns either Some[Person] or None
  }

  def main(argv: Array[String]): Unit = {
    val p = getPerson1
    if (p!=null) p.display

    getPerson2 match{
      case Some(person) => person.display
      case None => /* Do nothing */
    }
  }
}

Bây giờ, giả sử, phương thức getPerson1trả về null, sau đó lời gọi được thực hiện displaytrên dòng đầu tiên của mainbị ràng buộc không thành công với NPE. Tương tự nếu getPerson2trả về None, displaycuộc gọi sẽ lại thất bại với một số lỗi tương tự.

Nếu vậy, tại sao Scala lại làm phức tạp mọi thứ bằng cách giới thiệu một trình bao bọc giá trị mới ( Option[T]) thay vì làm theo một cách tiếp cận đơn giản được sử dụng trong Java?

CẬP NHẬT:

Tôi đã chỉnh sửa mã của mình theo đề xuất của @Mitch . Tôi vẫn không thể nhìn thấy bất kỳ lợi thế cụ thể của Option[T]. Tôi phải kiểm tra sự đặc biệt nullhoặc Nonetrong cả hai trường hợp. :(

Nếu tôi đã hiểu đúng từ câu trả lời của @ Michael , thì có phải lợi thế duy nhất của Option[T]nó là nó nói rõ ràng với lập trình viên rằng phương thức này có thể trả về Không ? Đây có phải là lý do duy nhất đằng sau sự lựa chọn thiết kế này?


23
Trên thực tế, phương thức "get" trong Tùy chọn [T] được phát âm là: "Tại sao mẫu bạn không khớp với cái này?"
Mitch Blevins

2
Mitch nói đúng. Cố gắng diễn đạt lại ví dụ của bạn mà không sử dụng get, và bạn sẽ hiểu . :-)
Daniel C. Sobral

Bạn có Person p .. is java .. .try val p = ... Ngoài ra, có nhiều hơn cho Option như được hiển thị bởi Daniel và Synesso bên dưới - một số câu trả lời TUYỆT VỜI ở đây.
Michael Neale

@Michael: Rất tiếc! Cảm ơn vì đã chỉ; đã sửa nó.
missfaktor

Câu trả lời:


72

Bạn sẽ đạt được điểm Optiontốt hơn nếu bạn buộc bản thân không bao giờ sử dụng get. Đó là vì gettương đương với "ok, send me back to null-land".

Vì vậy, hãy lấy ví dụ đó của bạn. Bạn sẽ gọi như thế nào nếu displaykhông sử dụng get? Đây là một số lựa chọn:

getPerson2 foreach (_.display)
for (person <- getPerson2) person.display
getPerson2 match {
  case Some(person) => person.display
  case _ =>
}
getPerson2.getOrElse(Person("Unknown", 0)).display

Không có lựa chọn thay thế nào trong số này sẽ cho phép bạn gọi displaythứ gì đó không tồn tại.

Về lý do gettồn tại, Scala không cho bạn biết mã của bạn nên được viết như thế nào. Nó có thể nhẹ nhàng thúc đẩy bạn, nhưng nếu bạn không muốn rơi vào lưới an toàn, đó là lựa chọn của bạn.


Bạn đã đóng đinh nó ở đây:

ưu điểm duy nhất của Option [T] là nó cho người lập trình biết rằng phương thức này có thể trả về None?

Ngoại trừ cái "duy nhất". Nhưng hãy để tôi trình bày lại điều đó theo một cách khác: ưu điểm chính của Option[T]over Tlà an toàn kiểu. Nó đảm bảo bạn sẽ không gửi một Tphương thức đến một đối tượng có thể không tồn tại, vì trình biên dịch sẽ không cho phép bạn.

Bạn đã nói rằng bạn phải kiểm tra tính null trong cả hai trường hợp, nhưng nếu bạn quên - hoặc không biết - bạn phải kiểm tra tính null, trình biên dịch có cho bạn biết không? Hay người dùng của bạn sẽ?

Tất nhiên, vì khả năng tương tác với Java, Scala cho phép null giống như Java. Vì vậy, nếu bạn sử dụng các thư viện Java, nếu bạn sử dụng các thư viện Scala được viết kém, hoặc nếu bạn sử dụng các thư viện Scala cá nhân được viết kém , bạn sẽ vẫn phải đối phó với các con trỏ null.

Hai lợi thế quan trọng khác của Optiontôi có thể nghĩ đến là:

  • Tài liệu: chữ ký kiểu phương thức sẽ cho bạn biết liệu một đối tượng có luôn được trả về hay không.

  • Khả năng phối ghép đơn lẻ.

Cái sau mất nhiều thời gian hơn để đánh giá đầy đủ và nó không phù hợp với các ví dụ đơn giản, vì nó chỉ thể hiện sức mạnh của nó trên mã phức tạp. Vì vậy, tôi sẽ đưa ra một ví dụ bên dưới, nhưng tôi biết rằng nó sẽ hầu như không có ý nghĩa gì ngoại trừ những người đã hiểu.

for {
  person <- getUsers
  email <- person.getEmail // Assuming getEmail returns Option[String]
} yield (person, email)

5
"force yourself to never, ever, use get" -> Nói cách khác: "You don't getit!" :)
fredoverflow

31

So sánh:

val p = getPerson1 // a potentially null Person
val favouriteColour = if (p == null) p.favouriteColour else null

với:

val p = getPerson2 // an Option[Person]
val favouriteColour = p.map(_.favouriteColour)

Thuộc tính monadic ràng buộc , xuất hiện trong Scala như bản đồ chức năng, cho phép chúng ta hoạt động chuỗi trên các đối tượng mà không lo lắng về việc liệu họ 'null' hay không.

Lấy ví dụ đơn giản này xa hơn một chút. Giả sử chúng tôi muốn tìm tất cả các màu yêu thích của danh sách mọi người.

// list of (potentially null) Persons
for (person <- listOfPeople) yield if (person == null) null else person.favouriteColour

// list of Options[Person]
listOfPeople.map(_.map(_.favouriteColour))
listOfPeople.flatMap(_.map(_.favouriteColour)) // discards all None's

Hoặc có lẽ chúng tôi muốn tìm tên của chị gái mẹ của cha một người:

// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister

// with options
val fathersMothersSister = getPerson2.flatMap(_.father).flatMap(_.mother).flatMap(_.sister)

Tôi hy vọng điều này làm sáng tỏ cách các lựa chọn có thể giúp cuộc sống dễ dàng hơn một chút.


Trong ví dụ cuối cùng của bạn, điều gì sẽ xảy ra nếu cha của người đó là trống? mapsẽ trở lại Nonevà cuộc gọi sẽ không thành công với một số lỗi. Nó tốt hơn nullcách tiếp cận như thế nào?
missfaktor

5
Không. Nếu một người là Không có (hoặc cha, mẹ hoặc chị), thì FatherMothersSister sẽ là Không, nhưng sẽ không có lỗi nào được đưa ra.
paradigmatic

6
Tôi nghĩ bạn muốn nói đến flatMap, hơn là bản đồ.
tên viết tắt

Cảm ơn vì Daniel đã chỉnh sửa. Tôi đã không thử mã trước khi đăng nó. Sẽ làm tốt hơn lần sau.
Synesso

2
val favouriteColour = if (p == null) p.favouriteColour else null // chính xác là lỗi mà Option giúp bạn tránh! Câu trả lời này đã ở đây trong nhiều năm mà không ai phát hiện ra lỗi này!
Mark Lister

22

Sự khác biệt là tinh tế. Hãy nhớ rằng để thực sự là một hàm, nó phải trả về một giá trị - null không thực sự được coi là "giá trị trả về bình thường" theo nghĩa đó, hơn nữa là một loại dưới cùng / không có gì.

Tuy nhiên, theo nghĩa thực tế, khi bạn gọi một hàm tùy chọn trả về một thứ gì đó, bạn sẽ làm như sau:

getPerson2 match {
   case Some(person) => //handle a person
   case None => //handle nothing 
}

Cấp, bạn có thể làm điều gì đó tương tự với null - nhưng điều này làm cho ngữ nghĩa của cuộc gọi getPerson2rõ ràng nhờ thực tế nó sẽ trả về Option[Person](một điều thực tế tốt đẹp, khác hơn là dựa vào một người nào đó đọc doc và nhận được một NPE vì họ không đọc doc).

Tôi sẽ thử và tìm hiểu một lập trình viên chức năng có thể đưa ra câu trả lời chặt chẽ hơn tôi có thể.


1
Đây cũng là hiểu biết của tôi về Quyền chọn. Nó nói rõ ràng với lập trình viên rằng chúng ta có thể nhận được Không có, và nếu bạn đủ ngu ngốc để nhớ thực hiện Một số (T) nhưng không nắm bắt được Không thì bạn cũng đang gặp rắc rối.
cflewis

1
Lewisham - Tôi nghĩ rằng trình biên dịch sẽ cung cấp cho bạn một cảnh báo vì Một số / Không có dạng dữ liệu đại số (đặc điểm kín trừu tượng ...) (nhưng tôi đang truy xuất từ ​​bộ nhớ ở đây).
Michael Neale

6
Điểm của kiểu Option trong hầu hết các ngôn ngữ sử dụng nó là thay vì ngoại lệ null thời gian chạy, bạn gặp lỗi kiểu thời gian biên dịch - trình biên dịch có thể biết rằng bạn không có hành động cho điều kiện Không có khi sử dụng dữ liệu, điều này sẽ là một lỗi loại.
Justin Smith,

15

Đối với tôi, các tùy chọn thực sự thú vị khi xử lý với cú pháp để hiểu. Lấy ví dụ trước synesso :

// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister

// with options
val fathersMothersSister = for {
                                  father <- person.father
                                  mother <- father.mother
                                  sister <- mother.sister
                               } yield sister

Nếu có bất kỳ sự chuyển nhượng nào None, ý fathersMothersSisterchí được Nonenhưng không NullPointerExceptionsẽ được nêu ra. Sau đó, bạn có thể chuyển một cách an toàn fathersMothersSisterđến một hàm lấy tham số Option mà không cần lo lắng. vì vậy bạn không kiểm tra null và bạn không quan tâm đến các trường hợp ngoại lệ. So sánh phiên bản này với phiên bản java được trình bày trong ví dụ synesso .


3
Thật tiếc là trong Scala, <-cú pháp bị giới hạn trong "cú pháp hiểu danh sách", vì nó thực sự giống với docú pháp chung hơn từ Haskell hoặc domonadbiểu mẫu từ thư viện đơn nguyên của Clojure. Ràng buộc nó vào danh sách bán nó ngắn.
seh

11
"Để hiểu" trong Scala về cơ bản là "làm" trong Haskell, chúng không giới hạn trong danh sách, bạn có thể sử dụng bất kỳ thứ gì triển khai: def map [B] (f: A => B): C [B] def flatMap [B] (f: A => C [B]): C [B] def filter (p: A => Boolean): C [A]. IOW, bất kỳ đơn nguyên
GClaramunt

2
@seh Tôi đã ủng hộ bình luận của @ GClaramunt, nhưng tôi không thể nhấn mạnh đủ quan điểm của anh ấy. Không mối liên hệ nào giữa danh sách để hiểu và danh sách trong Scala - ngoại trừ việc cái sau có thể sử dụng được với cái trước. Tôi giới thiệu bạn đến stackoverflow.com/questions/1052476/… .
Daniel C. Sobral

Vâng, tôi biết rằng không có mối quan hệ nào, nhưng tôi đồng ý rằng nó đáng được chỉ ra; Tôi đã bình luận về dòng đầu tiên của câu trả lời này, trong đó mô hình đề cập đến "cú pháp hiểu danh sách". Đó là một vấn đề giảng dạy, trái ngược với một vấn đề thiết kế ngôn ngữ.
seh

9

Bạn có khả năng sáng tác khá mạnh mẽ với Option:

def getURL : Option[URL]
def getDefaultURL : Option[URL]


val (host,port) = (getURL orElse getDefaultURL).map( url => (url.getHost,url.getPort) ).getOrElse( throw new IllegalStateException("No URL defined") )

Bạn có thể giải thích điều này hoàn toàn?
Jesvin Jose

8

Có thể ai đó đã chỉ ra điều này, nhưng tôi không thấy nó:

Một lợi thế của việc đối sánh mẫu với Option [T] so với kiểm tra null là Option là một lớp được niêm phong, vì vậy trình biên dịch Scala sẽ đưa ra cảnh báo nếu bạn sơ ý mã hóa một số trường hợp Không hoặc Một số. Có một cờ trình biên dịch đối với trình biên dịch sẽ biến cảnh báo thành lỗi. Vì vậy, có thể ngăn chặn việc không xử lý được trường hợp "không tồn tại" tại thời điểm biên dịch hơn là trong thời gian chạy. Đây là một lợi thế to lớn so với việc sử dụng giá trị null.


7

Nó không ở đó để giúp tránh kiểm tra rỗng, mà ở đó để buộc kiểm tra rỗng. Vấn đề trở nên rõ ràng khi lớp của bạn có 10 trường, hai trong số đó có thể là trống. Và hệ thống của bạn có 50 lớp tương tự khác. Trong thế giới Java, bạn cố gắng ngăn chặn các NPE trên các trường đó bằng cách sử dụng một số kết hợp của sức mạnh tinh thần, quy ước đặt tên hoặc thậm chí có thể là chú thích. Và mọi nhà phát triển Java đều thất bại ở mức độ này ở một mức độ đáng kể. Lớp Option không chỉ làm cho các giá trị "nullable" rõ ràng trực quan đối với bất kỳ nhà phát triển nào đang cố gắng hiểu mã mà còn cho phép trình biên dịch thực thi hợp đồng bất thành văn trước đây này.


6

[sao chép từ nhận xét này của Daniel Spiewak ]

Nếu cách duy nhất để sử dụng Optionlà so khớp mẫu để lấy ra các giá trị, thì vâng, tôi đồng ý rằng nó không cải thiện chút nào so với null. Tuy nhiên, bạn đang thiếu một lớp chức năng * khổng lồ * của nó. Lý do thuyết phục duy nhất để sử dụng Optionlà nếu bạn đang sử dụng các chức năng tiện ích bậc cao hơn của nó. Một cách hiệu quả, bạn cần phải sử dụng bản chất đơn nguyên của nó. Ví dụ: (giả sử một số lượng cắt tỉa API nhất định):

val row: Option[Row] = database fetchRowById 42
val key: Option[String] = row flatMap { _ get “port_key” }
val value: Option[MyType] = key flatMap (myMap get)
val result: MyType = value getOrElse defaultValue

Đó, không phải là tiện lợi sao? Chúng tôi thực sự có thể làm tốt hơn rất nhiều nếu chúng tôi sử dụng các forkhái niệm:

val value = for {
row <- database fetchRowById 42
key <- row get "port_key"
value <- myMap get key
} yield value
val result = value getOrElse defaultValue

Bạn sẽ nhận thấy rằng chúng tôi * không bao giờ * kiểm tra rõ ràng giá trị null, None hoặc bất kỳ ilk nào của nó. Toàn bộ điểm của Quyền chọn là tránh bất kỳ việc kiểm tra nào đó. Bạn chỉ cần xâu chuỗi các phép tính và di chuyển xuống dòng cho đến khi bạn * thực sự * cần lấy ra một giá trị. Tại thời điểm đó, bạn có thể quyết định xem bạn có muốn kiểm tra rõ ràng hay không (mà bạn không bao giờ phải làm), cung cấp giá trị mặc định, đưa ra một ngoại lệ, v.v.

Tôi chưa bao giờ, chưa bao giờ thực hiện bất kỳ đối sánh rõ ràng nào Optionvà tôi biết rất nhiều nhà phát triển Scala khác đang ở cùng một con thuyền. David Pollak đã đề cập với tôi vào ngày hôm trước rằng anh ấy sử dụng đối sánh rõ ràng như vậy trên Option(hoặc Box, trong trường hợp của Lift) như một dấu hiệu cho thấy nhà phát triển đã viết mã không hiểu đầy đủ về ngôn ngữ và thư viện chuẩn của nó.

Ý tôi không phải là một cái búa troll, nhưng bạn thực sự cần phải xem xét cách các tính năng ngôn ngữ * thực sự * được sử dụng trong thực tế như thế nào trước khi bạn coi chúng là vô dụng. Tôi hoàn toàn đồng ý rằng Option khá khó thuyết phục vì * bạn * đã sử dụng nó, nhưng bạn không sử dụng nó theo cách nó được thiết kế.


Có một hậu quả đáng buồn ở đây: không có bước nhảy dựa trên ngắn mạch trong vở kịch, để mọi tuyên bố liên tiếp kiểm tra các Optioncho Nonemột lần nữa. Nếu các câu lệnh được viết dưới dạng các điều kiện lồng nhau, mỗi "lỗi" tiềm ẩn sẽ chỉ được kiểm tra và xử lý một lần. Trong ví dụ của bạn, kết quả của fetchRowByIdđược kiểm tra hiệu quả ba lần: một lần để hướng dẫn keykhởi tạo, một lần nữa cho value's và cuối cùng cho result' s. Đó là một cách viết đơn giản, nhưng không phải là không có thời gian chạy của nó.
seh

4
Tôi nghĩ rằng bạn hiểu sai sự hiểu biết của Scala. Ví dụ thứ hai rõ ràng KHÔNG PHẢI là một vòng lặp, nó được trình biên dịch dịch thành một chuỗi các hoạt động flatMap - theo ví dụ đầu tiên.
Kevin Wright

Đã lâu rồi tôi không viết bình luận của mình ở đây, nhưng tôi chỉ thấy của Kevin. Kevin, bạn đã đề cập đến ai khi viết "bạn hiểu lầm?" Tôi không hiểu làm thế nào nó có thể là tôi , vì tôi chưa bao giờ đề cập bất cứ điều gì về một vòng lặp.
seh

6

Một điểm mà không ai khác ở đây dường như đã nêu ra là mặc dù bạn có thể có tham chiếu rỗng, nhưng có một sự khác biệt được giới thiệu bởi Option.

Đó là bạn có thể có Option[Option[A]], mà sẽ được nơi sinh sống của None, Some(None)Some(Some(a))nơi alà một trong những cư dân bình thường của A. Điều này có nghĩa là nếu bạn có một số loại vùng chứa và muốn có thể lưu trữ các con trỏ null trong đó và lấy chúng ra, bạn cần trả lại một số giá trị boolean bổ sung để biết liệu bạn có thực sự lấy ra một giá trị hay không. Mụn cóc như thế này có rất nhiều trong các API vùng chứa java và một số biến thể không có khóa thậm chí không thể cung cấp chúng.

null là một công trình xây dựng một lần, nó không tự biên soạn, chỉ có sẵn cho các loại tham chiếu và nó buộc bạn phải suy luận theo kiểu không tổng thể.

Ví dụ, khi bạn kiểm tra

if (x == null) ...
else x.foo()

bạn phải thực hiện trong đầu của bạn trong suốt elsechi nhánh đó x != nullvà điều này đã được kiểm tra. Tuy nhiên, khi sử dụng một cái gì đó như tùy chọn

x match {
case None => ...
case Some(y) => y.foo
}

bạn biết rằng y không phải Nonedo xây dựng - và bạn cũng biết nó không phải nullnhư vậy, nếu đó không phải là sai lầm hàng tỷ đô la của Hoare .


3

Tùy chọn [T] là một đơn nguyên, thực sự hữu ích khi bạn sử dụng các hàm bậc cao để thao tác các giá trị.

Tôi sẽ đề nghị bạn đọc các bài viết được liệt kê bên dưới, chúng thực sự là những bài báo hay cho bạn thấy tại sao Option [T] lại hữu ích và cách nó có thể được sử dụng theo cách chức năng.


Tôi sẽ thêm vào đề nghị đọc danh sách Tony Morris' công bố gần đây hướng dẫn 'gì Monad Mean?': Projects.tmorris.net/public/what-does-monad-mean/artifacts/1.0/...
Randall Schulz

3

Thêm vào lời giới thiệu của Randall về câu trả lời , hiểu lý do tại sao sự thiếu vắng tiềm năng của một giá trị được biểu thị bằng cách Optionđòi hỏi bạn phải hiểu những gì Optionchia sẻ với nhiều loại khác trong Scala — cụ thể là các loại mô hình mô hình hóa. Nếu một trong những đại diện cho sự vắng mặt của một giá trị bằng null, thì sự phân biệt vắng mặt đó không thể tham gia vào các hợp đồng được chia sẻ bởi các loại đơn nguyên khác.

Nếu bạn không biết monads là gì hoặc nếu bạn không nhận thấy chúng được thể hiện như thế nào trong thư viện của Scala, bạn sẽ không thấy những gì Optionsẽ phát cùng với những gì bạn đang bỏ lỡ. Có nhiều lợi ích khi sử dụng Optionthay vì null, điều đó sẽ đáng chú ý ngay cả khi không có bất kỳ khái niệm đơn nguyên nào (tôi thảo luận một số lợi ích trong số chúng trong chuỗi danh sách gửi thư người dùng scala "Cost of Option / Some vs null" ở đây ), nhưng nói về sự cô lập nó giống như nói về kiểu trình lặp của một danh sách liên kết cụ thể, tự hỏi tại sao nó lại cần thiết, đồng thời lại bỏ lỡ giao diện vùng chứa / trình lặp / thuật toán tổng quát hơn. Ở đây cũng có giao diện rộng hơn tại nơi làm việc,Option


Cám ơn rất nhiều về đường link. Nó thực sự hữu ích. :)
missfaktor

Nhận xét của bạn về chủ đề quá ngắn gọn, tôi gần như bỏ lỡ điểm của nó. Tôi thực sự ước null có thể bị cấm.
Alain O'Dea

2

Tôi nghĩ rằng chìa khóa được tìm thấy trong câu trả lời của Synesso: Option chủ yếu không hữu ích như một bí danh rườm rà cho null, mà là một đối tượng chính thức có thể giúp bạn giải quyết logic của mình.

Vấn đề với null là nó thiếu một đối tượng. Nó không có phương pháp nào có thể giúp bạn giải quyết nó (mặc dù là một nhà thiết kế ngôn ngữ, bạn có thể thêm danh sách các tính năng ngày càng dài vào ngôn ngữ của mình để mô phỏng một đối tượng nếu bạn thực sự cảm thấy thích nó).

Một điều mà Option có thể làm, như bạn đã trình bày, là giả lập null; sau đó bạn phải kiểm tra giá trị đặc biệt "Không có" thay vì giá trị đặc biệt "null". Nếu bạn quên, trong cả hai trường hợp, điều tồi tệ sẽ xảy ra. Tùy chọn làm cho nó ít có khả năng xảy ra tình cờ hơn, vì bạn phải nhập "get" (sẽ nhắc bạn rằng nó có thể là null, ờ, ý tôi là Không có), nhưng đây là một lợi ích nhỏ để đổi lấy một đối tượng wrapper bổ sung .

Nơi mà Option thực sự bắt đầu thể hiện sức mạnh của nó là giúp bạn đối phó với khái niệm tôi-muốn-cái gì đó-nhưng-tôi-không-thực-sự-có-một.

Hãy xem xét một số điều bạn có thể muốn làm với những thứ có thể không.

Có thể bạn muốn đặt giá trị mặc định nếu bạn có giá trị null. Hãy so sánh Java và Scala:

String s = (input==null) ? "(undefined)" : input;
val s = input getOrElse "(undefined)"

Thay cho một cấu trúc hơi rườm rà ?:, chúng ta có một phương thức xử lý ý tưởng "sử dụng giá trị mặc định nếu tôi là null". Điều này làm sạch mã của bạn một chút.

Có thể bạn chỉ muốn tạo một đối tượng mới nếu bạn có giá trị thực. So sánh:

File f = (filename==null) ? null : new File(filename);
val f = filename map (new File(_))

Scala ngắn hơn một chút và một lần nữa tránh được các nguồn gây lỗi. Sau đó, hãy xem xét lợi ích tích lũy khi bạn cần liên kết mọi thứ lại với nhau như trong các ví dụ của Synesso, Daniel và paradigmatic.

Nó không phải là một cải tiến lớn , nhưng nếu bạn thêm mọi thứ lên, nó rất đáng để lưu mã hiệu suất rất cao (nơi bạn muốn tránh ngay cả chi phí nhỏ khi tạo đối tượng Some (x) wrapper).

Việc sử dụng đối sánh không thực sự hữu ích ngoại trừ việc sử dụng như một thiết bị để cảnh báo bạn về trường hợp rỗng / Không có. Khi nó thực sự hữu ích là khi bạn bắt đầu xâu chuỗi nó, ví dụ: nếu bạn có một danh sách các tùy chọn:

val a = List(Some("Hi"),None,Some("Bye"));
a match {
  case List(Some(x),_*) => println("We started with " + x)
  case _ => println("Nothing to start with.")
}

Bây giờ bạn có thể gấp tất cả các trường hợp Không có và trường hợp Danh sách trống lại với nhau trong một câu lệnh tiện dụng để lấy ra chính xác giá trị bạn muốn.


2

Giá trị trả về rỗng chỉ hiện diện để tương thích với Java. Bạn không nên sử dụng chúng nếu không.


1

Nó thực sự là một câu hỏi về phong cách lập trình. Sử dụng Java hàm hoặc bằng cách viết các phương thức trợ giúp của riêng bạn, bạn có thể có chức năng Tùy chọn nhưng không được từ bỏ ngôn ngữ Java:

http://funcjava.org/examples/#Option.bind

Chỉ vì Scala bao gồm nó theo mặc định không làm cho nó trở nên đặc biệt. Hầu hết các khía cạnh của ngôn ngữ chức năng đều có sẵn trong thư viện đó và nó có thể cùng tồn tại độc đáo với các mã Java khác. Cũng như bạn có thể chọn lập trình Scala với null, bạn có thể chọn lập trình Java mà không có chúng.


0

Thừa nhận trước rằng đó là một câu trả lời lấp lánh, Option là một đơn nguyên.


Tôi biết đó là một đơn nguyên. Tại sao tôi lại đưa thẻ "đơn nguyên" vào câu hỏi?
missfaktor

^ Câu nói trên không có nghĩa là tôi hiểu đơn nguyên là gì. : D
missingfaktor

4
Tu viện là mát mẻ. Nếu bạn không sử dụng chúng hoặc ít nhất là không giả vờ hiểu thì bạn không được mát ;-)
paradigmatic

0

Thực sự tôi chia sẻ sự nghi ngờ với bạn. Về Option, nó thực sự làm tôi phiền lòng rằng 1) có một chi phí hiệu suất, vì có một số trình bao bọc "Một số" được tạo ra mỗi trang web. 2) Tôi phải sử dụng nhiều Some và Option trong mã của mình.

Vì vậy, để thấy ưu và nhược điểm của quyết định thiết kế ngôn ngữ này, chúng ta nên xem xét các lựa chọn thay thế. Vì Java chỉ bỏ qua vấn đề vô hiệu, nó không phải là một sự thay thế. Sự thay thế thực tế cung cấp ngôn ngữ lập trình Fantom. Có loại nullable và không thể nullable ở đó và?. ?: toán tử thay vì bản đồ của Scala / flatMap / getOrElse. Tôi thấy các gạch đầu dòng sau trong so sánh:

Ưu điểm của Option:

  1. ngôn ngữ đơn giản hơn - không yêu cầu cấu trúc ngôn ngữ bổ sung
  2. đồng nhất với các loại đơn nguyên khác

Ưu điểm của Nullable:

  1. cú pháp ngắn hơn trong các trường hợp điển hình
  2. hiệu suất tốt hơn (vì bạn không cần tạo các đối tượng Option và lambdas mới cho bản đồ, flatMap)

Vì vậy, không có người chiến thắng rõ ràng ở đây. Và một lưu ý nữa. Không có lợi thế về cú pháp chính cho việc sử dụng Option. Bạn có thể định nghĩa một cái gì đó như:

def nullableMap[T](value: T, f: T => T) = if (value == null) null else f(value)

Hoặc sử dụng một số chuyển đổi ngầm định để có được cú pháp nguyên tố với dấu chấm.


Có ai đã thực hiện các điểm chuẩn vững chắc về hiệu suất đạt được trên một máy ảo hiện đại không? Phân tích thoát có nghĩa là nhiều đối tượng Tùy chọn tạm thời có thể được phân bổ trên ngăn xếp (rẻ hơn nhiều so với đống) và GC thế hệ xử lý các đối tượng tạm thời kém hơn một chút khá hiệu quả. Tất nhiên nếu tốc độ quan trọng đối với dự án của bạn hơn là tránh NPE, thì các tùy chọn có thể không dành cho bạn.
Justin W

Không đề cập chi phí hiệu suất mà không có số để sao lưu nó. Đây là một sai lầm cực kỳ phổ biến khi lập luận chống lại những điều trừu tượng như Option. Tôi sẽ vui vẻ đảo ngược phiếu phản đối của mình nếu bạn chỉ đến hoặc xuất bản điểm chuẩn hoặc xóa nhận xét về hiệu suất :)
Alain O'Dea

0

Lợi thế thực sự của việc có các loại tùy chọn rõ ràng là bạn có thể không sử dụng chúng ở 98% tất cả các nơi, và do đó tĩnh loại trừ các trường hợp ngoại lệ rỗng. (Và trong 2% còn lại, hệ thống loại nhắc bạn kiểm tra đúng cách khi bạn thực sự truy cập chúng.)


-3

Một tình huống khác mà Option hoạt động, là trong các tình huống mà các kiểu không thể có giá trị null. Không thể lưu trữ null trong giá trị Int, Float, Double, v.v. nhưng với Option bạn có thể sử dụng None.

Trong Java, bạn sẽ cần sử dụng các phiên bản đóng hộp (Số nguyên, ...) của các loại đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.