it-swarm-vi.com

Cách tốt nhất để xử lý null trong Java?

Tôi có một số mã bị lỗi vì NullPulumException. Một phương thức đang được gọi trên đối tượng mà đối tượng không tồn tại.

Tuy nhiên, điều này khiến tôi suy nghĩ về cách tốt nhất để khắc phục điều này. Tôi có luôn luôn bảo vệ mã cho null để tôi chứng minh mã trong tương lai cho các ngoại lệ của con trỏ null hay tôi nên khắc phục nguyên nhân của null để nó không xảy ra xuôi dòng.

Quan điểm của bạn là gì?

21
Shaun F

Nếu null là một tham số đầu vào hợp lý cho phương thức của bạn, hãy sửa phương thức. Nếu không, sửa lỗi người gọi. "Hợp lý" là một thuật ngữ linh hoạt, vì vậy tôi đề xuất thử nghiệm sau: Phương thức nên xử lý đầu vào null như thế nào? Nếu bạn tìm thấy nhiều hơn một câu trả lời có thể, thì null là không một đầu vào hợp lý.

45
user281377

Không sử dụng null, sử dụng Tùy chọn

Như bạn đã chỉ ra, một trong những vấn đề lớn nhất với null in Java là nó có thể được sử dụng ở mọi nơi, hoặc ít nhất là cho tất cả các loại tài liệu tham khảo.

Không thể nói đó có thể là null và những gì không thể.

Java 8 giới thiệu một mẫu tốt hơn nhiều: Optional.

Và ví dụ từ Oracle:

String version = "UNKNOWN";
if(computer != null) {
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null) {
    USB usb = soundcard.getUSB();
    if(usb != null) {
      version = usb.getVersion();
    }
  }
}

Nếu mỗi trong số này có thể hoặc không thể trả về giá trị thành công, bạn có thể thay đổi API thành Optionals:

String name = computer.flatMap(Computer::getSoundcard)
    .flatMap(Soundcard::getUSB)
    .map(USB::getVersion)
    .orElse("UNKNOWN");

Bằng cách mã hóa rõ ràng tùy chọn trong loại, giao diện của bạn sẽ tốt hơn nhiều và mã của bạn sẽ sạch hơn.

Nếu bạn không sử dụng Java 8, bạn có thể xem com.google.common.base.Optional trong Google ổi.

Một lời giải thích hay của nhóm Guava: https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained

Một lời giải thích tổng quát hơn về những bất lợi đối với null, với các ví dụ từ một số ngôn ngữ: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/


@ Nonnull, @Nullable

Java 8 thêm các chú thích này để giúp các công cụ kiểm tra mã như IDE gặp vấn đề. Họ khá hạn chế về hiệu quả của họ.


Kiểm tra khi nào có ý nghĩa

Đừng viết 50% mã của bạn kiểm tra null, đặc biệt nếu không có gì hợp lý mà mã của bạn có thể làm với giá trị null.

Mặt khác, nếu null có thể được sử dụng và có nghĩa là một cái gì đó, hãy đảm bảo sử dụng nó.


Cuối cùng, rõ ràng bạn không thể xóa null khỏi Java. Tôi thực sự khuyên bạn nên thay thế trừu tượng Optional bất cứ khi nào có thể và kiểm tra null những thời điểm khác mà bạn có thể làm điều gì đó hợp lý về nó.

21
Paul Draper

Có nhiều cách để xử lý việc này, việc xử lý mã của bạn bằng if (obj != null) {} không lý tưởng, nó lộn xộn, nó gây thêm tiếng ồn khi đọc mã sau trong chu kỳ bảo trì và dễ bị lỗi, vì nó rất dễ bị quên để làm gói nồi hơi này.

Nó phụ thuộc vào việc bạn muốn mã tiếp tục thực thi lặng lẽ hay thất bại. Là null là một lỗi hoặc một điều kiện dự kiến.

Thế nào là null

Trong mọi trường hợp và trường hợp, Null thể hiện sự thiếu dữ liệu tuyệt đối. Nulls trong cơ sở dữ liệu đại diện cho việc thiếu một giá trị cho cột đó. về mặt lý thuyết, null String không giống với String trống, về lý thuyết, null int không giống với ZERO. Trong thực tế "nó phụ thuộc". Trống String có thể thực hiện Null Object Tốt cho lớp String, với Integer, nó phụ thuộc vào logic nghiệp vụ.

Các lựa chọn thay thế là:

  1. Mẫu Null Object . Tạo một thể hiện của đối tượng của bạn đại diện cho trạng thái null và khởi tạo tất cả các tham chiếu đến loại đó với tham chiếu đến việc triển khai Null. Điều này hữu ích cho các đối tượng loại giá trị đơn giản không có nhiều tham chiếu đến các đối tượng khác cũng có thể là null và được dự kiến ​​là null dưới dạng trạng thái hợp lệ.

  2. Sử dụng các công cụ Định hướng theo khía cạnh để dệt các phương thức với khía cạnh Null Checker Ngăn các tham số không có giá trị. Điều này dành cho các trường hợp null là một lỗi.

  3. Sử dụng assert() không tốt hơn nhiều so với if (obj != null){} nhưng ít nhiễu hơn.

  4. Sử dụng công cụ Thực thi Hợp đồng, chẳng hạn như Hợp đồng cho Java . Trường hợp sử dụng tương tự như một cái gì đó như AspectJ nhưng mới hơn và sử dụng Chú thích thay vì các tệp cấu hình bên ngoài. Tốt nhất trong cả hai tác phẩm của khía cạnh và khẳng định.

1 là giải pháp lý tưởng khi dữ liệu đến được biết là null và cần được thay thế bằng một số giá trị mặc định để người tiêu dùng ngược dòng sẽ không phải xử lý tất cả mã nồi hơi kiểm tra null. Kiểm tra đối với các giá trị mặc định đã biết cũng sẽ biểu cảm hơn.

2, 3 và 4 chỉ là các trình tạo Ngoại lệ thay thế thuận tiện để thay thế NullPointerException bằng một cái gì đó nhiều thông tin hơn, luôn luôn và cải tiến.

Cuối cùng

null in Java gần như trong mọi trường hợp là lỗi logic. Bạn phải luôn cố gắng loại bỏ nguyên nhân gốc của NullPointerExceptions. Bạn nên cố gắng không sử dụng null. $ = VAR #] điều kiện như logic nghiệp vụ. if (x == null) { i = someDefault; } chỉ cần thực hiện các xác nhận ban đầu cho thể hiện đối tượng mặc định đó.

8
user7519

Thêm kiểm tra null có thể làm cho thử nghiệm có vấn đề. Xem bài giảng tuyệt vời này ...

Kiểm tra bài giảng của Google Tech: "The Clean Code Talks - Đừng tìm kiếm thứ gì!" anh ấy nói về nó vào khoảng 24 phút

http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E

Lập trình hoang tưởng liên quan đến việc thêm kiểm tra null ở khắp mọi nơi. Có vẻ như là một ý tưởng tốt lúc đầu tuy nhiên từ góc độ thử nghiệm, nó làm cho việc kiểm tra loại kiểm tra null của bạn khó đối phó.

Ngoài ra, khi bạn tạo tiền đề cho sự tồn tại của một số đối tượng, chẳng hạn như

class House(Door door){

    .. null check here and throw exception if Door is NULL
    this.door = door
}

nó ngăn bạn tạo Nhà vì bạn sẽ ném ngoại lệ. Giả sử các trường hợp thử nghiệm của bạn đang tạo các đối tượng giả để thử nghiệm một thứ khác ngoài Cửa, tốt, bạn không thể làm điều này vì cửa là bắt buộc

Những người đã chịu đựng qua địa ngục tạo Mock đều nhận thức rõ về những loại phiền toái này.

Tóm lại, bộ thử nghiệm của bạn phải đủ mạnh để kiểm tra Cửa, Nhà, Mái nhà hoặc bất cứ thứ gì mà không cần phải hoang tưởng về nó. Seroiusly, thật khó để thêm một bài kiểm tra null cho các đối tượng cụ thể trong thử nghiệm của bạn :)

Bạn nên luôn luôn thích các ứng dụng hoạt động vì bạn có một số bài kiểm tra THỰC HIỆN nó hoạt động, thay vì HOPING nó hoạt động đơn giản vì bạn có cả đống kiểm tra null điều kiện ở khắp mọi nơi

8
Constantin

tl; dr - thật tốt khi kiểm tra bất ngờ nulls nhưng BAD cho một ứng dụng để cố gắng làm cho chúng tốt.

Chi tiết

Rõ ràng, có những tình huống trong đó null là đầu vào hoặc đầu ra hợp lệ cho một phương thức và các tình huống khác không có.

Quy tắc số 1:

Javadoc cho một phương thức cho phép tham số null hoặc trả về giá trị null phải ghi lại rõ ràng điều này và giải thích null nghĩa là gì.

Quy tắc số 2:

Không nên chỉ định phương thức API là chấp nhận hoặc trả về null trừ khi có lý do chính đáng để làm như vậy.

Đưa ra một đặc tả rõ ràng về "hợp đồng" vis-a-vis nulls của phương thức, đó là lỗi lập trình để vượt qua hoặc trả về null nơi bạn không nên.

Quy tắc số 3:

Một ứng dụng không nên cố gắng "làm tốt" các lỗi lập trình.

Nếu một phương thức phát hiện null không nên ở đó, thì nó không nên cố gắng khắc phục vấn đề bằng cách biến nó thành một thứ khác. Điều đó chỉ che giấu vấn đề từ các lập trình viên. Thay vào đó, nó nên cho phép NPE xảy ra và gây ra lỗi để lập trình viên có thể tìm ra nguyên nhân gốc rễ và khắc phục nó. Hy vọng sự thất bại sẽ được chú ý trong quá trình thử nghiệm. Nếu không, điều đó nói lên một cái gì đó về phương pháp thử nghiệm của bạn.

Quy tắc số 4:

Nếu khả thi, hãy viết mã của bạn để sớm phát hiện lỗi lập trình.

Nếu bạn gặp lỗi trong mã dẫn đến nhiều NPE, điều khó nhất có thể là tìm ra các giá trị null đến từ đâu. Một cách để giúp chẩn đoán dễ dàng hơn là viết mã của bạn để null được phát hiện càng sớm càng tốt. Thường thì bạn có thể làm điều này kết hợp với các kiểm tra khác; ví dụ.

public setName(String name) {
    // This also detects `null` as an (intended) side-effect
    if (name.length() == 0) {
        throw new IllegalArgumentException("empty name");
    }
}

(Rõ ràng có những trường hợp quy tắc 3 và 4 phải được tôi luyện với thực tế. Ví dụ (quy tắc 3), một số loại ứng dụng phải cố gắng tiếp tục sau phát hiện các lỗi lập trình có thể. Và (quy tắc 4) kiểm tra quá nhiều các tham số xấu có thể có tác động hiệu suất.)

4
Stephen C

Tôi khuyên bạn nên sửa phương pháp để phòng thủ. Ví dụ:

String go(String s){  
    return s.toString();  
}

Nên nhiều hơn theo dòng này:

String go(String s){  
    if(s == null){  
       return "";  
    }     
    return s.toString();  
}

Tôi nhận ra điều này là hoàn toàn tầm thường, nhưng nếu kẻ xâm phạm mong đợi một đối tượng cung cấp cho họ một đối tượng mặc định sẽ không khiến null bị bỏ qua.

3
Woot4Moo

Các quy tắc chung sau đây về null đã giúp tôi rất nhiều:

  1. Nếu dữ liệu đến từ bên ngoài sự kiểm soát của bạn, hãy kiểm tra một cách có hệ thống các giá trị null và hành động phù hợp. Điều này có nghĩa là ném một ngoại lệ có ý nghĩa đối với hàm (được kiểm tra hoặc không được kiểm tra, chỉ cần đảm bảo tên của ngoại lệ đó cho bạn biết chính xác những gì đang diễn ra.). Nhưng KHÔNG BAO GIỜ sợ một giá trị lỏng lẻo trong hệ thống của bạn có khả năng mang đến những điều bất ngờ.

  2. Nếu Null nằm trong miền của các giá trị phù hợp cho mô hình dữ liệu của bạn, hãy xử lý nó một cách thích hợp.

  3. Khi trả về giá trị cố gắng không trả về null bất cứ khi nào có thể. Luôn thích danh sách trống, chuỗi rỗng, mẫu Null Object. Giữ null dưới dạng giá trị được trả về khi đây là biểu diễn dữ liệu tốt nhất có thể cho trường hợp sử dụng nhất định.

  4. Có lẽ là quan trọng nhất trong tất cả ... Thử nghiệm, thử nghiệm và thử nghiệm lại. Khi kiểm tra mã của bạn, đừng kiểm tra nó với tư cách là một lập trình viên, hãy kiểm tra nó như một kẻ thống trị tâm thần của Đức quốc xã và cố gắng tưởng tượng ra mọi cách để hành hạ địa ngục khỏi mã đó.

Điều này có xu hướng một chút về phía hoang tưởng liên quan đến null thường dẫn đến mặt tiền và proxy có hệ thống giao tiếp với thế giới bên ngoài và các giá trị được kiểm soát chặt chẽ ở bên trong với sự dư thừa dồi dào. Thế giới bên ngoài ở đây có nghĩa là hầu hết mọi thứ tôi không tự viết mã. Nó chịu một thời gian chạy chi phí nhưng cho đến nay tôi hiếm khi phải tối ưu hóa điều này bằng cách tạo ra "phần an toàn null" của mã. Mặc dù vậy, tôi phải nói rằng tôi chủ yếu tạo ra các hệ thống chạy dài để chăm sóc sức khỏe và điều cuối cùng tôi muốn là hệ thống con giao diện mang dị ứng i-ốt của bạn đến máy quét CT vì một con trỏ null bất ngờ vì một người khác trong hệ thống khác không bao giờ nhận ra rằng tên có thể chứa dấu nháy đơn hoặc ký tự như 但 耒耨

dù sao .... 2 xu của tôi

2
Newtopian

Tôi sẽ đề xuất sử dụng Tùy chọn/Một số/Không có mẫu từ các ngôn ngữ chức năng. Tôi không phải là Java chuyên gia, nhưng tôi chuyên sâu sử dụng triển khai mẫu này của riêng tôi trong dự án C # của tôi và tôi chắc chắn rằng nó có thể được chuyển đổi thành Java thế giới.

Ý tưởng đằng sau mẫu này là: nếu có tình huống logic khi có khả năng vắng mặt một giá trị (ví dụ: khi truy xuất từ ​​cơ sở dữ liệu theo id), bạn cung cấp một đối tượng loại Tùy chọn [T], trong đó T là giá trị có thể . Tôi trường hợp vắng mặt của một đối tượng giá trị của lớp Không [T] được trả về, trong trường hợp nếu sự tồn tại của giá trị - đối tượng của Một số [T] được trả về, có chứa giá trị.

Trong trường hợp này, bạn phải xử lý khả năng vắng mặt giá trị và nếu bạn xem lại mã, bạn có thể dễ dàng tìm thấy dấu hiệu xử lý sai. Để lấy cảm hứng từ việc triển khai ngôn ngữ C #, hãy tham khảo kho lưu trữ bitbucket của tôi https://bitbucket.org/mikegirkin/optionsomenone

Nếu bạn trả về giá trị null và nó tương đương một cách hợp lý với lỗi (ví dụ: không có tệp nào tồn tại hoặc không thể kết nối), bạn nên ném ngoại lệ hoặc sử dụng một mẫu xử lý lỗi khác. Ý tưởng đằng sau điều này, một lần nữa, bạn kết thúc bằng giải pháp, khi bạn phải xử lý tình huống vắng mặt giá trị và có thể dễ dàng tìm thấy nơi xử lý sai trong mã.

1
Hedin
  1. Không sử dụng loại Tùy chọn, trừ khi nó thực sự là tùy chọn, thường thì lối thoát được xử lý tốt hơn như một ngoại lệ, trừ khi bạn thực sự mong đợi null là một tùy chọn, và không phải vì bạn thường xuyên viết mã lỗi.

  2. Vấn đề không phải là null như một loại mà nó có công dụng của nó, như bài viết của Google chỉ ra. Vấn đề là null nên được kiểm tra và xử lý, thường thì chúng có thể được thoát ra một cách duyên dáng.

  3. Có một số trường hợp null thể hiện các điều kiện không hợp lệ trong các khu vực bên ngoài hoạt động thường xuyên của chương trình (đầu vào của người dùng không hợp lệ, sự cố cơ sở dữ liệu, lỗi mạng, tệp bị thiếu, dữ liệu bị hỏng), đó là những trường hợp ngoại lệ được kiểm tra, xử lý chúng, thậm chí nếu nó chỉ để đăng nhập nó.

  4. Xử lý ngoại lệ được cho phép các ưu tiên và đặc quyền hoạt động khác nhau trong JVM, trái ngược với một loại, như Tùy chọn, có ý nghĩa đối với bản chất đặc biệt của sự xuất hiện của chúng, bao gồm hỗ trợ sớm cho việc tải trình xử lý được ưu tiên vào bộ nhớ, vì bạn không cần nó treo xung quanh tất cả các thời gian.

  5. Bạn không cần phải viết trình xử lý null ở mọi nơi, chỉ ở nơi chúng có khả năng xảy ra, bất cứ nơi nào bạn có khả năng truy cập dịch vụ dữ liệu không đáng tin cậy và vì hầu hết những điều này rơi vào một vài mẫu chung có thể dễ dàng được trừu tượng hóa mà bạn thực sự chỉ cần một cuộc gọi xử lý, trừ trường hợp hiếm.

Vì vậy, tôi đoán câu trả lời của tôi sẽ được bọc trong một ngoại lệ được kiểm tra và xử lý nó, hoặc sửa mã không đáng tin cậy nếu nó nằm trong khả năng của bạn.

0
J-Boss

Chà, nếu một trong những kết quả có thể có của phương pháp của bạn là giá trị null thì bạn nên mã hóa một cách phòng thủ cho điều đó, nhưng nếu một phương thức được cho là trả về giá trị không null nhưng tôi sẽ không sửa nó.

Như thường lệ, nó phụ thuộc vào từng trường hợp, như với hầu hết mọi thứ trong cuộc sống :)

0
jonezy

Các thư viện của Apache Commons ' lang cung cấp cách xử lý các giá trị null

phương thức default IfNull trong lớp ObjectUtils cho phép bạn trả về giá trị mặc định nếu đối tượng được truyền là null

0
Mahmoud Hossam