Lý do Optional
đã được thêm vào Java là vì điều này:
return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.findFirst()
.getOrThrow(() -> new InternalError(...));
sạch hơn thế này:
Method matching =
Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.getFirst();
if (matching == null)
throw new InternalError("Enclosing method not found");
return matching;
Quan điểm của tôi là Tùy chọn được viết để hỗ trợ lập trình chức năng , được thêm vào Java cùng một lúc. (Ví dụ được cung cấp bởi một blog của Brian Goetz . Một ví dụ tốt hơn có thể sử dụng orElse()
phương thức này, vì mã này sẽ đưa ra một ngoại lệ, nhưng bạn sẽ có được hình ảnh.)
Nhưng bây giờ, mọi người đang sử dụng Tùy chọn vì một lý do rất khác. Họ đang sử dụng nó để giải quyết một lỗ hổng trong thiết kế ngôn ngữ. Lỗ hổng là thế này: Không có cách nào để chỉ định tham số và giá trị trả về nào của API được phép là null. Nó có thể được đề cập trong javadocs, nhưng hầu hết các nhà phát triển thậm chí không viết javadocs cho mã của họ và không nhiều người sẽ kiểm tra javadocs khi họ viết. Vì vậy, điều này dẫn đến rất nhiều mã luôn kiểm tra các giá trị null trước khi sử dụng chúng, mặc dù chúng thường không thể là null vì chúng đã được xác thực liên tục chín hoặc mười lần trong ngăn xếp cuộc gọi.
Tôi nghĩ rằng đã có một khao khát thực sự để giải quyết lỗ hổng này, bởi vì rất nhiều người nhìn thấy lớp Tùy chọn mới cho rằng mục đích của nó là để thêm sự rõ ràng cho các API. Đó là lý do tại sao mọi người đặt câu hỏi như "getters có nên trả về Tùy chọn không?" Không, có lẽ họ không nên, trừ khi bạn mong đợi getter được sử dụng trong lập trình chức năng, điều này rất khó xảy ra. Trong thực tế, nếu bạn nhìn vào nơi Tùy chọn được sử dụng trong API Java, thì chủ yếu là trong các lớp Stream, vốn là cốt lõi của lập trình chức năng. (Tôi đã không kiểm tra rất kỹ, nhưng các lớp Stream có thể là nơi duy nhất chúng được sử dụng.)
Nếu bạn có kế hoạch sử dụng một getter trong một chút mã chức năng, thì có thể có một getter tiêu chuẩn và một thứ hai trả về Tùy chọn.
Ồ, và nếu bạn cần lớp của mình được tuần tự hóa, bạn tuyệt đối không nên sử dụng Tùy chọn.
Tùy chọn là một giải pháp rất tệ cho lỗ hổng API vì a) chúng rất dài dòng và b) Chúng không bao giờ có ý định giải quyết vấn đề đó ngay từ đầu.
Một giải pháp tốt hơn cho lỗ hổng API là Trình kiểm tra Nullness . Đây là bộ xử lý chú thích cho phép bạn chỉ định tham số nào và giá trị trả về được phép là null bằng cách chú thích chúng với @Nullable. Bằng cách này, trình biên dịch có thể quét mã và tìm hiểu xem một giá trị thực sự có thể là null đang được chuyển đến một giá trị không cho phép null. Theo mặc định, nó cho rằng không có gì được phép là null trừ khi nó được chú thích như vậy. Bằng cách này, bạn không phải lo lắng về các giá trị null. Truyền giá trị null cho tham số sẽ dẫn đến lỗi trình biên dịch. Việc kiểm tra một đối tượng cho null không thể tạo ra cảnh báo trình biên dịch. Tác dụng của việc này là thay đổi NullPulumException từ lỗi thời gian chạy sang lỗi thời gian biên dịch.
Điều này thay đổi mọi thứ.
Đối với getters của bạn, không sử dụng tùy chọn. Và cố gắng thiết kế các lớp học của bạn để không thành viên nào có thể là null. Và có thể thử thêm Trình kiểm tra Nullness vào dự án của bạn và khai báo getters và setter tham số @Nullable nếu họ cần. Tôi chỉ thực hiện điều này với các dự án mới. Nó có thể tạo ra rất nhiều cảnh báo trong các dự án hiện có được viết với rất nhiều bài kiểm tra không cần thiết cho null, vì vậy có thể rất khó để trang bị thêm. Nhưng nó cũng sẽ bắt được rất nhiều lỗi. Tôi thích nó. Mã của tôi sạch hơn và đáng tin cậy hơn vì nó.
(Ngoài ra còn có một ngôn ngữ mới giải quyết vấn đề này. Kotlin, biên dịch thành mã byte Java, cho phép bạn chỉ định nếu một đối tượng có thể là null khi bạn khai báo nó. Đó là một cách tiếp cận sạch hơn.)
Phụ lục cho bài gốc (phiên bản 2)
Sau khi suy nghĩ rất nhiều, tôi miễn cưỡng đưa ra kết luận rằng có thể chấp nhận trả lại Tùy chọn với một điều kiện: Giá trị được truy xuất có thể thực sự là null. Tôi đã thấy rất nhiều mã nơi mọi người thường xuyên trả về Tùy chọn từ các getters mà không thể trả về null. Tôi thấy đây là một thực hành mã hóa rất tệ, chỉ làm tăng thêm độ phức tạp cho mã, điều này làm cho các lỗi có nhiều khả năng hơn. Nhưng khi giá trị trả về thực sự có thể là null, hãy tiếp tục và bọc nó trong Tùy chọn.
Hãy nhớ rằng các phương thức được thiết kế để lập trình chức năng và yêu cầu tham chiếu chức năng, sẽ (và nên) được viết dưới hai dạng, một trong số đó sử dụng Tùy chọn. Ví dụ, Optional.map()
và Optional.flatMap()
cả hai đều tham khảo chức năng. Cái đầu tiên lấy tham chiếu đến một getter thông thường, và cái thứ hai lấy một cái trả về Tùy chọn. Vì vậy, bạn sẽ không làm bất cứ ai ủng hộ bằng cách trả lại Tùy chọn trong đó giá trị không thể là null.
Đã nói tất cả, tôi vẫn thấy cách tiếp cận được sử dụng bởi Trình kiểm tra Nullness là cách tốt nhất để xử lý null, vì chúng biến NullPulumExceptions từ lỗi thời gian chạy sang biên dịch lỗi thời gian.