Điểm của lớp học Tùy chọn của Ổi là gì


89

Gần đây tôi đã đọc về điều này và thấy mọi người sử dụng lớp này, nhưng trong hầu hết các trường hợp, việc sử dụng nullcũng sẽ hoạt động - nếu không muốn nói là trực quan hơn. Ai đó có thể cung cấp một ví dụ cụ thể về nơi Optionalsẽ đạt được điều gì đó nullkhông thể hoặc theo cách sạch hơn nhiều không? Điều duy nhất tôi có thể nghĩ đến là sử dụng nó với Mapskhông chấp nhận nullkhóa, nhưng thậm chí điều đó có thể được thực hiện với một "ánh xạ" bên giá trị của null. Bất cứ ai có thể cung cấp cho tôi một lý lẽ thuyết phục hơn? Cảm ơn bạn.


12
ngẫu nhiên rant: tôi ghét nó khi mọi người lạm dụng một "khuôn mẫu" và làm cho mã trông thật xấu xí vì một số lợi ích lý thuyết không tồn tại ...
RAY

Đối với Java8, tôi sẽ không sử dụng lớp Guava này nữa vì nó kém mạnh hơn lớp JDK. Xem stackoverflow.com/a/10756992/82609
Sebastien Lorber

Câu trả lời:


155

Thành viên nhóm ổi đây.

Có lẽ nhược điểm lớn nhất duy nhất nulllà không rõ nghĩa của nó trong bất kỳ ngữ cảnh cụ thể nào: nó không có tên minh họa. Không phải lúc nào điều đó cũng hiển nhiên nullcó nghĩa là "không có giá trị cho tham số này" - heck, như một giá trị trả về, đôi khi nó có nghĩa là "lỗi", hoặc thậm chí "thành công" (!!), hoặc đơn giản là "câu trả lời đúng là không có gì". Optionalthường là khái niệm bạn thực sự muốn nói khi bạn tạo một biến nullable, nhưng không phải lúc nào cũng vậy. Khi không, chúng tôi khuyên bạn nên viết lớp của riêng mình, tương tự Optionalnhưng với một cách đặt tên khác, để làm rõ ý bạn thực sự muốn nói.

Nhưng tôi có thể nói rằng lợi thế lớn nhất của Optionalnó không nằm ở tính dễ đọc: lợi thế là khả năng chống ngu ngốc của nó. Nó buộc bạn phải chủ động suy nghĩ về trường hợp vắng mặt nếu bạn muốn chương trình của mình biên dịch, vì bạn phải chủ động mở gói Optionalvà giải quyết trường hợp đó. Null khiến việc quên mọi thứ trở nên dễ dàng một cách đáng lo ngại và mặc dù FindBugs có ích, tôi không nghĩ rằng nó gần như giải quyết được vấn đề. Điều này đặc biệt có liên quan khi bạn đang trả về các giá trị có thể có hoặc không có "hiện tại". Bạn (và những người khác) có nhiều khả năng quên điều đó other.method(a, b)có thể trả về một nullgiá trị hơn là bạn có khả năng quên điều đó acó thể xảy ra nullkhi bạn đang triển khaiother.method . Trở vềOptional làm cho người gọi không thể quên trường hợp đó, vì họ phải tự mình mở gói đồ vật.

Vì những lý do này, chúng tôi khuyên bạn nên sử dụng Optionallàm kiểu trả về cho các phương thức của mình, nhưng không nhất thiết phải có trong các đối số phương thức của bạn.

(Nhân tiện, đây là hoàn toàn cũi, từ cuộc thảo luận ở đây .)


3
+1 cho một lời giải thích tuyệt vời. Tôi không biết đây có phải là nguồn cảm hứng hay không, nhưng tôi sẽ thêm một con trỏ vào các loại tùy chọn trong SML, OCaml và F #, có nhiều ngữ nghĩa giống nhau.
Adam Mihalcin

2
Luôn luôn tuyệt vời khi nhận được câu trả lời từ người có liên quan trực tiếp. Tôi đã +1 chỉ cho điều đó. Sẽ +1 thêm cho một câu trả lời tuyệt vời. (nhưng tôi không thể.) Tôi nghĩ quan điểm về việc lấy nó làm giá trị trả lại để buộc người tiêu dùng sử dụng một phương pháp không quen thuộc phải nỗ lực thừa nhận rằng "không có gì" có thể được trả lại là một lý do thuyết phục. Tôi không thích mặc dù nó đang được sử dụng quá mức (hoặc thậm chí bị lạm dụng). ví dụ: tôi cảm thấy thực sự không thoải mái khi thấy mọi người đưa Tùy chọn làm giá trị vào Bản đồ. Bản đồ khôi phục null cho một khóa không tồn tại / không được ánh xạ là một mô hình được thiết lập tốt ... Không cần phải làm cho nó phức tạp hơn ...
RAY

9
Nó được thiết lập tốt để Maptrả về nullnếu một khóa không được ánh xạ, nhưng hãy nhớ lại rằng nếu bạn làm như vậy map.put(key, null), thì map.containsKey(key)sẽ trả về truenhưng map.get(key)sẽ quay lại null. Optionalcó thể hữu ích trong việc xóa bỏ sự khác biệt giữa trường hợp "được ánh xạ rõ ràng thành null" và trường hợp "không có trong bản đồ". Tôi cho rằng điều đó Optionalcó thể bị lạm dụng, nhưng tôi vẫn chưa tin rằng trường hợp bạn mô tả là lạm dụng.
Louis Wasserman

8
Để sử dụng một phép loại suy cổ xưa, đã từng có những thứ khổng lồ này được gọi là "danh bạ điện thoại" :-) và nếu tôi yêu cầu bạn tra cứu số của ai đó và bạn nói "không có số cho người đó", tôi sẽ nói "bạn làm gì có nghĩa là, họ ở trong đó với một số không công khai hoặc bạn chỉ không thể tìm thấy mục nhập cho họ? " Hai phản hồi có thể có ánh xạ tương ứng với Optional.absent () và null. Optional.absent () là "phủ định tích cực".
Kevin Bourrillion

3
@RAY: Trong một cách, Optional<T> rằng "tìm thấy, nhưng không hợp lệ" giá trị. Hay chính xác hơn, Optional<T>là một cách để trang trí bất kỳ kiểu nào Tcó thêm giá trị "tìm thấy, nhưng không hợp lệ" - tạo ra một kiểu mới bằng cách kết hợp hai kiểu hiện có. Nếu bạn có một trăm lớp, có thể sẽ trở nên lộn xộn khi phải tạo một giá trị "tìm thấy, nhưng không hợp lệ" riêng cho từng lớp, nhưng Optional<T>có thể dễ dàng làm việc cho tất cả chúng.
Daniel Pryden

9

Nó thực sự trông giống như Maybemô hình Monad từ Haskell.

Bạn nên đọc phần sau, Wikipedia Monad (lập trình chức năng) :

Và đọc Từ Tùy chọn đến Đơn nguyên với Ổi trên Blog của Kerflyn, thảo luận về Tùy chọn Ổi được sử dụng làm Đơn nguyên:


Chỉnh sửa: Với Java8, có một Tùy chọn tích hợp có các toán tử đơn nguyên như flatMap. Đây là một chủ đề gây tranh cãi nhưng cuối cùng đã được thực hiện.

Xem http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

public Optional<String> tryFindSimilar(String s)  //...

Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar =       opt.flatMap(this::tryFindSimilar);

Nhà flatMapđiều hành cần thiết cho phép các hoạt động đơn lẻ và cho phép dễ dàng chuỗi các cuộc gọi mà tất cả đều trả về các kết quả Tùy chọn.

Hãy nghĩ về nó, nếu bạn sử dụng maptoán tử 5 lần, bạn sẽ kết thúc bằng một Optional<Optional<Optional<Optional<Optional<String>>>>>, trong khi sử dụng flatMapsẽ cho bạnOptional<String>

Vì Java8, tôi không muốn sử dụng Tùy chọn của Guava kém mạnh mẽ hơn.


6

Một lý do tốt để sử dụng nó là nó làm cho nulls của bạn rất có ý nghĩa. Thay vì trả về giá trị null có thể có nghĩa là nhiều thứ (như lỗi, hoặc thất bại, hoặc trống, v.v.), bạn có thể đặt 'tên' cho null của mình. Hãy xem ví dụ này:

cho phép xác định một POJO cơ bản:

class PersonDetails {

String person;
String comments;

public PersonDetails(String person, String comments) {
    this.person = person;
    this.comments = comments;
}

public String getPerson() {
    return person;
}


public String getComments() {
    return comments;
}

}

Bây giờ chúng ta hãy sử dụng POJO đơn giản này:

public Optional<PersonDetails> getPersonDetailstWithOptional () {

  PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless,
  lets make the return value more meaningful*/


    if (details == null) {
      //return an absent here, caller can check for absent to signify details are not present
        return Optional.absent();
    } else {
      //else return the details wrapped in a guava 'optional'
        return Optional.of(details);   
    }
}

Bây giờ, hãy tránh sử dụng null và thực hiện kiểm tra của chúng tôi với Tùy chọn để nó có ý nghĩa

public void checkUsingOptional () {

    Optional<PersonDetails> details = getPersonDetailstWithOptional();

    /*below condition checks if persons details are present (notice we dont check if person details are null,
    we use something more meaningful. Guava optional forces this with the implementation)*/
    if (details.isPresent()) {

      PersonDetails details = details.get();

        // proceed with further processing
        logger.info(details);

    } else {
        // do nothing
        logger.info("object was null"); 
    }

    assertFalse(details.isPresent());
}

do đó cuối cùng nó là một cách để làm cho null có ý nghĩa và ít mơ hồ hơn.


4

Ưu điểm quan trọng nhất của Tùy chọn là nó bổ sung thêm chi tiết vào hợp đồng giữa người triển khai và người gọi một hàm. Vì lý do này, cả hai đều hữu ích cho các tham số và kiểu trả về.

Nếu bạn thực hiện quy ước luôn luôn có Optionalcác đối tượng rỗng, bạn sẽ thêm giải thích rõ hơn cho các trường hợp như:

  1. Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)

    Hợp đồng ở đây quy định rõ ràng rằng có khả năng một kết quả không được trả lại nhưng cũng cho thấy rằng nó sẽ hoạt động có fromtokhông có.

  2. Optional<Integer> maxPrime(Optional<Integer> from, Integer to)

    Hợp đồng chỉ định rằng from là tùy chọn vì vậy giá trị vắng mặt có thể có ý nghĩa đặc biệt như bắt đầu từ 2. Tôi có thể mong đợi rằng giá trị null cho totham số sẽ ném ra một ngoại lệ.

Vì vậy, phần tốt của việc sử dụng Tùy chọn là hợp đồng trở nên vừa mang tính mô tả (tương tự với @NotNullchú thích) nhưng cũng chính thức vì bạn phải viết mã .get()để đối phó Optional.


Tại sao lại sử dụng <Danh sách> tùy chọn khi Danh sách trống sẽ sạch hơn?
RAY

Tôi đã chọn sai ví dụ khi quay trở lại. Với các tập hợp, bạn có thể thực hiện quy ước luôn trả về một tập hợp rỗng thay vì một giá trị null. Nếu quy ước này được sử dụng, bạn không cần tùy chọn cho các bộ sưu tập. Tùy chọn có thể được coi là tập hợp không có hoặc một phần tử nên không cần đặt nó xung quanh tập hợp khác.
raisercostin

2
tùy thuộc vào ngữ cảnh, có thể có sự khác biệt có ý nghĩa giữa danh sách có 0 mục và danh sách vắng mặt.
plasma147
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.