Tìm nạp một giá trị mà không phải kiểm tra null trong Java


15

Nhiều lần tôi thấy mình không kiểm tra khi tìm nạp một giá trị từ một số phân cấp dữ liệu để tránh NullPulumExceptions, điều mà tôi thấy dễ bị lỗi và cần rất nhiều bản tóm tắt.

Tôi đã viết một thói quen rất đơn giản cho phép tôi bỏ qua việc kiểm tra null khi tìm nạp một đối tượng ...

public final class NoNPE {

    public static <T> T get(NoNPEInterface<T> in) {
        try {
            return in.get();
        } catch (NullPointerException e) {
            return null;
        }
    }

    public interface NoNPEInterface<T> {
        T get();
    }
}

Tôi sử dụng nó một chút như thế này ...

Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());

Điều trên dẫn đến việc tôi nhận được một đối tượng Room hoặc null, mà không phải kiểm tra null tất cả các cấp độ cha.

Bạn nghĩ gì về những điều trên? Tôi đang tạo ra một mô hình có vấn đề? Có cách nào tốt hơn để làm điều này theo ý kiến ​​của bạn?


1
Như bạn rõ ràng đang sử dụng Java 8, tôi có thể đề nghị bạn xem xét thiết kế lại ứng dụng của mình để sử dụng java.util.Optionalthay vì null để thể hiện dữ liệu bị thiếu không? Điều này cung cấp các tiện ích tiện dụng cho cả trường hợp bạn mô tả và các trường hợp bạn muốn tiếp tục với dữ liệu mặc định thay vì chỉ trả lại một điều kiện thất bại ở cuối chuỗi ..
Periata Breatta

Tôi nghĩ rằng về cơ bản bạn đã khám phá lại Option(hoặc Maybe) đơn nguyên :)
Andres F.

Có thể trả về Tùy chọn thay vì T hoặc null: bằng cách này, bạn có thể sử dụng phương thức orElse () trực tiếp. 18 tháng sau, nhưng có thể giúp ai đó.
Benj

Cách tiếp cận khác được đề cập trong bài viết này illegalargumentexception.blogspot.com/2015/03/... , một trong số họ đang sử dụng một thư viện tên kludje trong đó có một cú pháp rất thú vị
Benj

Câu trả lời:


13

Giải pháp của bạn rất thông minh. Vấn đề tôi thấy là thực tế là bạn không biết tại sao bạn lại bị null? Có phải vì nhà không có phòng? Có phải vì thị trấn không có nhà? Có phải vì đất nước không có thị trấn? Có phải vì có một nullvị trí 0 trong bộ sưu tập vì có lỗi ngay cả khi có nhà ở vị trí 1 trở lên?

Nếu bạn sử dụng NonPElớp mở rộng , bạn sẽ gặp vấn đề gỡ lỗi nghiêm trọng. Tôi nghĩ rằng tốt hơn là biết chính xác chuỗi bị hỏng ở đâu hơn là âm thầm nhận được một nulllỗi có thể ẩn một lỗi sâu hơn.

Ngoài ra, điều này vi phạm Luật Demeter : country.getTown().getHouses().get(0).getLivingRoom(). Thường xuyên hơn không, vi phạm một số nguyên tắc tốt khiến bạn phải thực hiện các giải pháp không chính thống để giải quyết vấn đề gây ra bởi vi phạm nguyên tắc đó.

Lời khuyên của tôi là bạn nên sử dụng nó một cách thận trọng và thử giải quyết lỗ hổng thiết kế khiến bạn phải gánh chịu sự cố chống tàu hỏa (vì vậy bạn không phải sử dụng NonPEở mọi nơi). Nếu không, bạn có thể có lỗi sẽ khó phát hiện.


Câu trả lời chính xác. Có, tôi sẽ không biết nơi tôi có null trong chuỗi. Trong nhiều trường hợp mặc dù tôi không quan tâm và không phải kiểm tra null có nghĩa là mã dễ đọc hơn và ít bị lỗi nồi hơi hơn. Nhưng vâng, bạn đúng trong một số trường hợp tôi cần đưa ra quyết định hợp lý khác nếu đối tượng cha là null, thì điều này sẽ gây ra vấn đề. Phương thức thông thường hoặc lớp Tùy chọn có thể là một giải pháp an toàn hơn ở đó.
Eurig Jones

Nói chung, khi sử dụng Optionđơn nguyên, bạn không quan tâm giá trị vắng mặt ở đâu trong chuỗi. Khi bạn quan tâm đến nó, có lẽ bạn sẽ sử dụng một loại khác, chẳng hạn như Either.
Andres F.

Cách tiếp cận của OP tương tự như C # 6 ?.và các ?[]nhà khai thác. Một ví dụ về thời điểm bạn có thể muốn sử dụng một thứ như vậy là cài đặt phía máy chủ phân cấp. var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;Ai quan tâm tại sao bất kỳ phần nào trong số đó là null? Có lẽ bạn không thể tìm nạp các cài đặt. Có lẽ bạn đã quyết định loại bỏ một phần của cài đặt. Trong mọi trường hợp, bạn không bao giờ thực sự có thể tin tưởng vào việc nhận cài đặt của máy chủ, do đó, mặc định thường là một ý tưởng tốt và bạn không quan tâm tại sao bạn không thể có được cài đặt thực tế trừ khi nó xảy ra rất thường xuyên khi không nên .
chris

Bây giờ tôi không nói rằng điều đó tốt hơn hay tệ hơn là bản địa hóa các mặc định và chỉ đơn giản là lấy lại giá trị bạn muốn thông qua các truy cập thông thường settings.a.b.c. Sau đó, một lần nữa, đây là một ví dụ đơn lẻ.
chris

10

Ý tưởng là tốt, thực sự tốt trong thực tế. Vì Java 8 các Optionalloại tồn tại, nên có thể tìm thấy giải thích chi tiết tại loại Tùy chọn Java . Một ví dụ với những gì bạn đã đăng là

Optional.ofNullable(country)
    .map(Country::getTown)
    .map(Town::Houses);

Và hơn thế nữa.


1
Có, tôi đã biết về lớp Tùy chọn, từ cả Java 8 và Guava và chúng thực sự hữu ích. Nhưng bạn không thể tìm nạp một đối tượng như bình thường, làm cho mã khó đọc hơn một chút và cũng kém hiệu quả hơn một chút. Nhưng nhược điểm là có nhiều toán tử rất hữu ích mà lớp Tùy chọn cung cấp.
Eurig Jones

3
@EurigJones Tôi không nghĩ mã trở nên kém hiệu quả hơn. Khả năng đọc trong mắt của kẻ si tình, nhưng tôi cho rằng đó Optionallà giải pháp dễ đọc hơn của cả hai, nếu chỉ vì - không giống như đề xuất của bạn - đó là một thành ngữ rất phổ biến . Nó thậm chí còn súc tích hơn của bạn!
Andres F.

0

Phương pháp của bạn hoạt động đủ tốt cho mục đích dự định của nó, mặc dù trả về nulls khi bạn nhận được NullPointerExceptionâm thanh như thiết kế xấu.

Cố gắng tránh nulls khi bạn có thể và chỉ vượt qua chúng khi chúng đại diện cho một cái gì đó hoặc có ý nghĩa đặc biệt và chỉ trả lại chúng khi chúng đại diện / có nghĩa là một cái gì đó - nếu không bạn nên ném một NullPointerException. Điều này tránh lỗi và nhầm lẫn. Nếu Objectkhông nên null, NullPointernên ném. Nếu một đối tượng có thể nullthì sẽ không có gì sai khi một đối tượng được truyền vào. Nếu không thì phương thức của bạn ở trên hoạt động.


0

Tôi có thể cảm thấy nỗi đau của bạn, nhưng giải pháp đề xuất là một ý tưởng tồi.

  • Nếu một trong các getters ném NPE vì một số lý do khác, bạn sẽ bỏ qua nó.
  • Có nguy cơ cho lambda bên trong đó phát triển thành mã khủng khiếp. Ví dụ, nếu có một yêu cầu mới để trả về một hằng số đặc biệt khi không có nhà trong thị trấn, một lập trình viên lười biếng có thể mở rộng lamda, bỏ lại mọi thứ NoNPE.get.
  • Như đã đề cập, Optional.maplà những gì bạn đang tìm kiếm.
  • Hình phạt của việc tạo một phiên bản mới của NullPulumException thường rất đáng kể. Đó là nhiều micro giây, đặc biệt là khi ngăn xếp cuộc gọi của bạn ngày càng lớn hơn. Thật khó để dự đoán nơi tiện ích của bạn sẽ được sử dụng.

Như một lưu ý phụ, NoNPEInterfacelà một bản sao của java.util.function.Supplier.

Trong một số trường hợp, bạn có thể cân nhắc sử dụng các tiện ích đánh giá biểu thức có trong nhiều khung (ví dụ: EL, SpEL):

evaluateProperty(country, "town.houses[0].livingRoom")

Ok cho các mẫu trang web, nhưng thường phát triển chậm (không kiểm tra thời gian biên dịch) và chạy chậm.
kevin cline
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.