2012-03-14 32 views
22

Tôi học Scala một thời gian và không thể hiểu rõ cách sử dụng Tùy chọn. Nó giúp tôi tránh kiểm tra null khi tôi chuỗi chức năng (theo docs). Điều đó rõ ràng đối với tôi :)Khi nào sử dụng Option

Tiếp theo tôi thấy rằng Tùy chọn có thể là loại chỉ báo cho nhà phát triển mà giá trị null có thể ở đây và nó phải được xử lý. Có đúng không? Nếu có, tôi có nên sử dụng Tùy chọn nếu có thể không? Ví dụ:

class Racer { 
    val car = new Car() 
} 

Tôi có lớp đua và tôi chắc chắn trường ô tô không thể rỗng (vì nó không đổi và nhận giá trị khi tạo cá thể Racer). Không cần lựa chọn ở đây.

class Racer { 
    var car = new Car() 
} 

Ở đây tôi làm cho nó để chiếc xe có thể thay đổi. Và nó có thể cho ai đó gán null vào ô tô. Tôi có nên sử dụng Tùy chọn tại đây không? Nếu có, tôi nhận thấy rằng tất cả các lĩnh vực lớp học của tôi là ứng cử viên cho Tùy chọn. Và mã của tôi trông giống như thế này

class Racer { 
    var car: Option[Car] = None 
    var currentRace: Option[Race] = None 
    var team: Option[Team] = None 
    ... 
} 

Trông nó có đẹp không? Đối với tôi, có vẻ như loại Tùy chọn đang lạm dụng.

def foo(): Result = { 
    if(something) 
     new Result() 
    else 
     null 
} 

Tôi có phương thức có thể trả về giá trị rỗng. Tôi có nên trả lại tùy chọn thay thế không? Tôi có nên luôn làm điều đó nếu có thể cho phương thức trả về null không?

Bất kỳ suy nghĩ nào về nó cũng sẽ hữu ích. Cảm ơn trước!

Câu hỏi của tôi tương tự như Why option nhưng tôi nghĩ nó không giống nhau. Đó là khoảng khi nào, không phải là lý do. :)

+1

ngoài các câu trả lời khác, hãy thử đọc http://simply.liftweb.net/index-7.2.html –

+0

Cảm ơn bạn. Tôi sẽ xem qua một chút. – Soteric

Trả lời

34

Bạn nên tránh null càng nhiều càng tốt trong Scala. Nó thực sự chỉ tồn tại cho khả năng tương tác với Java. Vì vậy, thay vì null, hãy sử dụng Option bất cứ khi nào có thể hàm hoặc phương thức có thể trả về "không có giá trị" hoặc khi hợp lệ về mặt logic cho biến thành viên có "không có giá trị".

Đối với ví dụ Racer: Có phải là Racer thực sự hợp lệ nếu không có car, currentRaceteam? Nếu không, thì bạn không nên thực hiện các tùy chọn biến thành viên đó. Không chỉ làm cho họ lựa chọn vì nó về mặt lý thuyết có thể gán chúng cho null; chỉ làm điều đó nếu hợp lý bạn xem nó là một đối tượng hợp lệ Racer nếu bất kỳ biến thành viên nào không có giá trị.

Nói cách khác, tốt nhất là giả vờ như thể null không tồn tại. Việc sử dụng null trong mã Scala là một mùi mã.

def foo(): Option[Result] = if (something) Some(new Result()) else None 

Lưu ý rằng Option có nhiều phương pháp hữu ích để làm việc.

val opt = foo() 

// You can use pattern matching 
opt match { 
    case Some(result) => println("The result is: " + result) 
    case None   => println("There was no result!") 
} 

// Or use for example foreach 
opt foreach { result => println("The result is: " + result) } 

Ngoài ra, nếu bạn muốn chương trình theo phong cách chức năng, bạn nên tránh dữ liệu có thể thay đổi càng nhiều càng tốt, có nghĩa là: tránh việc sử dụng các var, sử dụng val thay vào đó, sử dụng các bộ sưu tập bất biến và làm cho lớp học của riêng bạn bất biến càng nhiều càng tốt.

+0

Câu trả lời hay. Có ý nghĩa. Cảm ơn. – Soteric

+11

Ngoài "giả vờ [như]' null' không tồn tại ", nếu bạn phải đối phó với mã Java cung cấp cho bạn' foo' có thể là 'null', bạn có thể sử dụng' Option (foo) ' , sẽ biến 'null' thành' None' và bất kỳ thứ gì khác thành 'Some (blah)'. –

6

Ý tưởng đằng sau Options là để loại bỏ null, vì vậy có, bạn nên sử dụng chúng thay vì trả lại "null hoặc giá trị". Đó cũng là lý do tại sao, nếu bạn muốn mô hình các trường tùy chọn (mối quan hệ 0..1 với các đối tượng khác), sử dụng Option chắc chắn là một điều tốt.

Hạn chế nhỏ nhất là nó hiển thị khai báo lớp với nhiều trường tùy chọn một chút tiết, như bạn đã đề cập.

Một điều nữa, trong scala, bạn được khuyến khích sử dụng "đối tượng không thay đổi", vì vậy trong ví dụ của bạn, các trường phải là một số val s. ;)

+0

Trong trường hợp tôi sử dụng val làm thế nào tôi có thể thay đổi giá trị? Nếu tôi muốn có một chiếc xe mới cho tay đua của tôi thì sao? – Soteric

+4

Ý tưởng là xây dựng một đối tượng mới khi một sự kiện như một chiếc xe mới xuất hiện: 'class Racer (tên: String, car: Option [Car]) {def receiveANewCar (c: Car) = new Racer (tên, Some (c))); def brokeHisCar = new Racer (tên, Không)} ' – Nicolas

9

Khi nói đến lập trình chức năng ở Scala, Option thích hợp hơn null vì nó an toàn loại và phát đẹp với các cấu trúc khác trong mô hình chức năng.

Đặc biệt, bạn có thể dễ dàng viết mã idiomatic bằng các hàm bậc cao trên Option. Điều này Scala Option Cheat Sheet là một đọc hữu ích về chủ đề này.

7

Trong khi Option có thể làm cho lớp của bạn có định nghĩa chi tiết hơn, thay thế là không biết khi nào bạn cần kiểm tra xem biến có được xác định hay không. Tôi nghĩ rằng trong ví dụ của bạn, nếu lớp học của bạn là không thay đổi (tất cả các trường là val s), bạn sẽ không cần quá nhiều Option s.

Đối với phương thức foo, nếu bạn trả về giá trị rỗng, bạn cần ghi lại tài liệu và khách hàng cần đọc tài liệu đó. Sau đó, khách hàng sẽ viết một loạt các mã như:

val result = foo(x) 
if (result != null) { 
    println(result) 
} 

Nếu thay vào đó bạn xác định foo để trả lại một Option[Result], các hệ thống kiểu buộc khách hàng để xử lý các khả năng của một kết quả không xác định. Họ cũng có toàn bộ sức mạnh của các lớp sưu tập.

result foreach println 

Một lợi thế nữa của việc sử dụng phương pháp bộ sưu tập với một Option thay vì thử nghiệm cho null là nếu phương pháp của bạn được mở rộng đến một bộ sưu tập nhiều nguyên tố (chẳng hạn như một List), bạn có thể không cần phải thay đổi code. Ví dụ: ban đầu bạn có thể giả định Racer chỉ có thể có một phi hành đoàn duy nhất, do đó bạn xác định val crew: Option[Crew] và bạn có thể kiểm tra xem phi hành đoàn có trên 30 tuổi với crew.forall(_.age > 30).getOrElse(false) hay không. Bây giờ nếu bạn thay đổi định nghĩa thành val crew: List[Crew] mã cũ của bạn sẽ vẫn biên dịch và bây giờ bạn sẽ kiểm tra xem tất cả các thành viên phi hành đoàn của bạn có trên 30.

Đôi khi bạn cần xử lý null vì thư viện bạn đang sử dụng có thể trả lại. Ví dụ: khi bạn nhận được tài nguyên, bạn có thể nhận được kết quả null. May mắn thay, nó rất dễ dàng để bảo vệ kết quả bọc để họ được chuyển thành một lựa chọn.

val resource = Option(this.getClass.getResource("foo")) 

Nếu getResource trở null sau đó resource bằng None, và nếu không nó là một Some[URL]. Mát mẻ!

Thật không may, Option có thể có một số chi phí do nó liên quan đến việc tạo đối tượng bổ sung. Tuy nhiên, chi phí trên là tối thiểu so với một ngoại lệ con trỏ null!

+0

Để tạo một lớp bất biến, bạn có đề xuất tạo bản sao của một cá thể mỗi lần tôi muốn thay đổi nó không? Như Nicolas đã nói. – Soteric

+1

Có, nhưng điều này phụ thuộc phần nào vào cách bạn đang sử dụng đối tượng. Nếu bạn có một đối tượng có nhiều trường mà bạn thay đổi thường xuyên, không thay đổi có thể không có ý nghĩa. Tuy nhiên, bạn thường có thể thiết kế mã của mình khác nhau để bạn thực hiện ít đột biến hơn. Nếu bạn cần đột biến, bạn có thể ẩn tính đột biến thông qua một đặc điểm (ví dụ: Danh sách có trường có thể thay đổi - bạn không thể truy cập vào nó). – schmmd

+0

+1 cho ví dụ ứng dụng khách –

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