Tránh! = Null câu lệnh


4015

Tôi sử dụng object != nullrất nhiều để tránh NullPointerException.

Có một sự thay thế tốt cho điều này?

Ví dụ tôi thường sử dụng:

if (someobject != null) {
    someobject.doCalc();
}

Này kiểm tra cho một NullPointerExceptioncho someobjectđối tượng trong đoạn mã trên.

Lưu ý rằng câu trả lời được chấp nhận có thể đã hết hạn, xem https://stackoverflow.com/a/2386013/12943 để biết cách tiếp cận gần đây hơn.


120
@Shervin Khuyến khích null làm cho mã ít dễ hiểu và kém tin cậy hơn.
Tom Hawtin - tackline

44
Các toán tử Elvis đã được đề xuất nhưng có vẻ như nó sẽ không có trong Java 7 . Quá tệ, ?. ?: và? [] là những người tiết kiệm thời gian đáng kinh ngạc.
Scott

72
Không sử dụng null là vượt trội so với hầu hết các đề xuất khác ở đây. Ném ngoại lệ, không trả lại hoặc cho phép null. BTW - từ khóa 'khẳng định' là vô dụng, vì nó bị tắt theo mặc định. Sử dụng cơ chế thất bại luôn được kích hoạt
ianpojman

13
Đây là một lý do tại sao bây giờ tôi đang sử dụng Scala. Trong Scala mọi thứ không thể rỗng. Nếu bạn muốn cho phép vượt qua hoặc trả về "không có gì", thì bạn phải sử dụng Tùy chọn [T] thay vì chỉ đối số T als hoặc kiểu trả về.
Thekwasti 2/214

11
@thSoft Thật vậy, Optionloại tuyệt vời của Scala bị hủy hoại bởi sự không sẵn sàng kiểm soát hoặc không cho phép của ngôn ngữ null. Tôi đã đề cập điều này trên hackernews và đã bị đánh giá thấp và nói rằng "dù sao cũng không ai sử dụng null trong Scala". Đoán xem, tôi đã tìm thấy null trong Scala do đồng nghiệp viết. Vâng, họ đang "làm sai" và phải được giáo dục về nó, nhưng thực tế vẫn là hệ thống loại ngôn ngữ sẽ bảo vệ tôi khỏi điều này và nó không :(
Andres F.

Câu trả lời:


2644

Điều này đối với tôi nghe có vẻ như là một vấn đề khá phổ biến mà các nhà phát triển từ trung cấp đến trung cấp có xu hướng phải đối mặt vào một lúc nào đó: họ không biết hoặc không tin vào các hợp đồng mà họ đang tham gia và phòng thủ quá mức cho null. Ngoài ra, khi viết mã của riêng họ, họ có xu hướng dựa vào trả về null để chỉ ra điều gì đó do đó yêu cầu người gọi kiểm tra null.

Nói cách khác, có hai trường hợp kiểm tra null xuất hiện:

  1. Trong đó null là một phản hồi hợp lệ về mặt hợp đồng; và

  2. Trường hợp đó không phải là một phản hồi hợp lệ.

(2) là dễ dàng. Sử dụng các assertcâu lệnh (xác nhận) hoặc cho phép thất bại (ví dụ: NullPulumException ). Các xác nhận là một tính năng Java được sử dụng nhiều đã được thêm vào trong 1.4. Cú pháp là:

assert <condition>

hoặc là

assert <condition> : <object>

trong đó <condition>một biểu thức boolean và <object>là một đối tượng có toString()đầu ra của phương thức sẽ được bao gồm trong lỗi.

Một assertcâu lệnh ném một Error( AssertionError) nếu điều kiện không đúng. Theo mặc định, Java bỏ qua các xác nhận. Bạn có thể kích hoạt các xác nhận bằng cách chuyển tùy chọn -eacho JVM. Bạn có thể kích hoạt và vô hiệu hóa các xác nhận cho các lớp và gói riêng lẻ. Điều này có nghĩa là bạn có thể xác thực mã với các xác nhận trong khi phát triển và thử nghiệm và vô hiệu hóa chúng trong môi trường sản xuất, mặc dù thử nghiệm của tôi đã cho thấy bên cạnh không có tác động hiệu suất từ ​​các xác nhận.

Không sử dụng các xác nhận trong trường hợp này là OK vì mã sẽ thất bại, đó là điều sẽ xảy ra nếu bạn sử dụng các xác nhận. Sự khác biệt duy nhất là với các xác nhận nó có thể xảy ra sớm hơn, theo cách có ý nghĩa hơn và có thể có thêm thông tin, điều này có thể giúp bạn tìm ra lý do tại sao nó xảy ra nếu bạn không mong đợi nó.

(1) khó hơn một chút. Nếu bạn không kiểm soát được mã bạn đang gọi thì bạn bị kẹt. Nếu null là một phản hồi hợp lệ, bạn phải kiểm tra nó.

Tuy nhiên, nếu đó là mã mà bạn kiểm soát (và đây thường là trường hợp), thì đó là một câu chuyện khác. Tránh sử dụng null làm phản hồi. Với các phương thức trả về các bộ sưu tập, thật dễ dàng: trả về các bộ sưu tập trống (hoặc mảng) thay vì null khá nhiều lần.

Với các bộ sưu tập không phải là khó hơn. Hãy xem đây là một ví dụ: nếu bạn có các giao diện này:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

trong đó Parser lấy đầu vào của người dùng thô và tìm thấy việc cần làm, có lẽ nếu bạn đang thực hiện giao diện dòng lệnh cho một cái gì đó. Bây giờ bạn có thể làm cho hợp đồng trở lại vô hiệu nếu không có hành động thích hợp. Điều đó dẫn đến việc kiểm tra null mà bạn đang nói đến.

Một giải pháp thay thế là không bao giờ trả về null và thay vào đó sử dụng mẫu Null Object :

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

Đối chiếu:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

đến

ParserFactory.getParser().findAction(someInput).doSomething();

đó là một thiết kế tốt hơn nhiều vì nó dẫn đến mã ngắn gọn hơn.

Điều đó nói rằng, có lẽ nó hoàn toàn phù hợp với phương thức findAction () để ném Ngoại lệ với thông báo lỗi có ý nghĩa - đặc biệt trong trường hợp này bạn đang dựa vào đầu vào của người dùng. Sẽ tốt hơn nhiều nếu phương thức findAction đưa ra một Ngoại lệ so với phương thức gọi để làm nổ tung với một NullPulumException đơn giản mà không có lời giải thích.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

Hoặc nếu bạn nghĩ rằng cơ chế thử / bắt quá xấu, thay vì Không làm gì, hành động mặc định của bạn sẽ cung cấp phản hồi cho người dùng.

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

631
Tôi không đồng ý với tuyên bố của bạn về hành động DO_NOTHING. Nếu phương thức tìm hành động không thể tìm thấy một hành động, thì trả về null là điều nên làm. Bạn đã "tìm thấy" một hành động trong mã của mình mà không thực sự được tìm thấy, vi phạm nguyên tắc của phương thức, để tìm một hành động có thể sử dụng được.
MetroidFan2002

266
Tôi đồng ý rằng null được sử dụng quá mức trong Java, đặc biệt là với các danh sách. Vì vậy, nhiều apis sẽ tốt hơn nếu họ trả về một danh sách / mảng / bộ sưu tập trống thay vì null. Nhiều lần, null được sử dụng trong trường hợp nên ném ngoại lệ. Một ngoại lệ nên được ném nếu trình phân tích cú pháp không thể phân tích cú pháp.
Laplie Anderson

92
Ví dụ sau ở đây là, IIRC, mẫu thiết kế Null Object.
Steven Evers

62
@Cshah (và MetroidFan2002). Đơn giản, hãy đặt nó vào hợp đồng và sau đó rõ ràng là một hành động trả lại không tìm thấy sẽ không làm gì cả. Nếu đây là thông tin quan trọng đối với người gọi, thì hãy cung cấp một cách để khám phá rằng đó là một hành động không tìm thấy (tức là cung cấp một phương thức để kiểm tra xem kết quả có phải là đối tượng DO_NOTHING không). Ngoài ra, nếu hành động thường được tìm thấy thì bạn vẫn không nên trả về null mà thay vào đó hãy ném một ngoại lệ chỉ ra điều kiện đó - điều này vẫn dẫn đến mã tốt hơn. Nếu muốn, cung cấp một phương thức riêng trả về boolean để kiểm tra xem hành động có tồn tại không.
Kevin Brock

35
Súc tích không bằng mã chất lượng. Tôi xin lỗi bạn nghĩ vậy. Mã của bạn che giấu các tình huống trong đó một lỗi sẽ là lợi thế.
gshauger

635

Nếu bạn sử dụng (hoặc lập kế hoạch sử dụng) một IDE Java như JetBrains IntelliJ IDEA , Eclipse hoặc Netbeans hoặc một công cụ như findbugs thì bạn có thể sử dụng các chú thích để giải quyết vấn đề này.

Về cơ bản, bạn đã có @Nullable@NotNull.

Bạn có thể sử dụng trong phương thức và tham số, như thế này:

@NotNull public static String helloWorld() {
    return "Hello World";
}

hoặc là

@Nullable public static String helloWorld() {
    return "Hello World";
}

Ví dụ thứ hai sẽ không được biên dịch (trong IntelliJ IDEA).

Khi bạn sử dụng helloWorld()hàm đầu tiên trong một đoạn mã khác:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

Bây giờ trình biên dịch IntelliJ IDEA sẽ cho bạn biết rằng kiểm tra là vô ích, vì helloWorld()hàm sẽ không nullbao giờ trở lại .

Sử dụng tham số

void someMethod(@NotNull someParameter) { }

nếu bạn viết một cái gì đó như:

someMethod(null);

Điều này sẽ không được biên dịch.

Ví dụ cuối sử dụng @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Làm cái này

iWantToDestroyEverything().something();

Và bạn có thể chắc chắn rằng điều này sẽ không xảy ra. :)

Đó là một cách hay để cho trình biên dịch kiểm tra một cái gì đó nhiều hơn bình thường và để thực thi các hợp đồng của bạn mạnh hơn. Thật không may, nó không được hỗ trợ bởi tất cả các trình biên dịch.

Trong IntelliJ IDEA 10.5 trở đi, họ đã thêm hỗ trợ cho bất kỳ @Nullable @NotNulltriển khai nào khác .

Xem bài đăng trên blog Các chú thích linh hoạt và có thể định cấu hình hơn @ Nullable / @ NotNull .


120
@NotNull, @NullableVà chú thích nullness khác là một phần của JSR 305 . Bạn cũng có thể sử dụng chúng để phát hiện các vấn đề tiềm ẩn với các công cụ như FindBugs .
Jacek S

32
Tôi thấy khó chịu một cách kỳ lạ là các giao diện @NotNull& @Nullablesống trong gói com.sun.istack.internal. (Tôi đoán tôi liên kết com.sun với các cảnh báo về việc sử dụng API độc quyền.)
Jonik

20
khả năng di chuyển mã trở nên vô hiệu với jetbrains. Tôi sẽ nghĩ hai lần (vuông) trước khi buộc xuống mức ide. Giống như Jacek S nói rằng họ là một phần của JSR theo bất kỳ cách nào mà tôi nghĩ là JSR303.
Java Ka Baby

10
Tôi thực sự không nghĩ rằng sử dụng trình biên dịch tùy chỉnh là một giải pháp khả thi cho vấn đề này.
Shivan Dragon

64
Điểm hay của các chú thích, @NotNull@Nullablechúng là xuống cấp một cách độc đáo khi mã nguồn được xây dựng bởi một hệ thống không hiểu chúng. Vì vậy, về mặt hiệu quả, lập luận rằng mã không thể di động có thể không hợp lệ - nếu bạn sử dụng một hệ thống hỗ trợ và hiểu các chú thích này, bạn sẽ nhận được lợi ích bổ sung của việc kiểm tra lỗi nghiêm ngặt hơn, nếu không bạn sẽ nhận được ít hơn nhưng mã của bạn vẫn nên xây dựng tốt và chất lượng của chương trình đang chạy của bạn là CÙNG, vì những chú thích này không được thi hành trong thời gian chạy. Ngoài ra, tất cả các trình biên dịch là tùy chỉnh ;-)
20/03/13

321

Nếu giá trị null không được phép

Nếu phương thức của bạn được gọi bên ngoài, hãy bắt đầu với một cái gì đó như thế này:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

Sau đó, trong phần còn lại của phương thức đó, bạn sẽ biết rằng đó objectkhông phải là null.

Nếu đó là một phương thức nội bộ (không phải là một phần của API), chỉ cần tài liệu rằng nó không thể là null và đó là phương thức.

Thí dụ:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

Tuy nhiên, nếu phương thức của bạn chỉ chuyển giá trị trên và phương thức tiếp theo truyền vào nó, v.v ... nó có thể gặp vấn đề. Trong trường hợp đó bạn có thể muốn kiểm tra đối số như trên.

Nếu null được cho phép

Điều này thực sự phụ thuộc. Nếu thấy rằng tôi thường làm một cái gì đó như thế này:

if (object == null) {
  // something
} else {
  // something else
}

Vì vậy, tôi phân nhánh, và làm hai điều hoàn toàn khác nhau. Không có đoạn mã xấu xí, bởi vì tôi thực sự cần phải làm hai việc khác nhau tùy thuộc vào dữ liệu. Ví dụ, tôi nên làm việc với đầu vào, hay tôi nên tính một giá trị mặc định tốt?


Nó thực sự hiếm khi tôi sử dụng thành ngữ " if (object != null && ...".

Có thể dễ dàng hơn để cung cấp cho bạn các ví dụ, nếu bạn hiển thị các ví dụ về nơi bạn thường sử dụng thành ngữ.


94
Điểm nào trong việc ném IllegalArgumentException? Tôi nghĩ rằng NullPulumException sẽ rõ ràng hơn và điều đó cũng sẽ bị ném nếu bạn không tự kiểm tra null. Tôi sẽ sử dụng khẳng định hoặc không có gì cả.
Axel

23
Không chắc là mọi giá trị khác ngoài null đều được chấp nhận. Bạn có thể có IllegalArgumentException, OutOfRageException, v.v. Đôi khi điều này có ý nghĩa. Những lần khác, cuối cùng bạn tạo ra rất nhiều lớp ngoại lệ không thêm bất kỳ giá trị nào, sau đó bạn chỉ cần sử dụng IllegalArgumentException. Không có nghĩa là có một ngoại lệ cho đầu vào null và một ngoại lệ khác cho mọi thứ khác.
myplacesk

7
Có, tôi đồng ý với nguyên tắc không nhanh, nhưng trong ví dụ đã nêu ở trên, giá trị không được truyền vào, nhưng là đối tượng mà phương thức sẽ được gọi. Vì vậy, nó thất bại nhanh như nhau và việc thêm một kiểm tra null chỉ để ném một ngoại lệ sẽ được ném ra cùng một lúc và địa điểm dường như không làm cho việc gỡ lỗi trở nên dễ dàng hơn.
Axel

8
Một lỗ hổng bảo mật? JDK có đầy đủ mã như thế này. Nếu bạn không muốn người dùng thấy stacktraces, thì hãy tắt chúng đi. Không ai ngụ ý rằng hành vi không được ghi lại. MySQL được viết bằng C trong đó con trỏ null hội thảo là hành vi không xác định, không có gì giống như ném một ngoại lệ.
fgb

8
throw new IllegalArgumentException("object==null")
Thorbjørn Ravn Andersen

238

Ồ, tôi gần như ghét phải thêm một câu trả lời khác khi chúng tôi có 57 cách khác nhau để đề xuất NullObject pattern, nhưng tôi nghĩ rằng một số người quan tâm đến câu hỏi này có thể muốn biết rằng có một đề xuất trên bảng cho Java 7 để thêm "null-safe xử lý " Cú pháp được sắp xếp hợp lý cho logic if-not-bằng-null.

Ví dụ được đưa ra bởi Alex Miller trông như thế này:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

?.nghĩa là chỉ tham chiếu định danh bên trái nếu nó không rỗng, nếu không thì đánh giá phần còn lại của biểu thức là null. Một số người, như Dick Wall, thành viên của Java Posse và các cử tri tại Devoxx thực sự thích đề xuất này, nhưng cũng có ý kiến ​​phản đối, với lý do nó thực sự sẽ khuyến khích sử dụng nhiều hơn nullnhư một giá trị trọng tâm.


Cập nhật: Một đề xuất chính thức cho một toán tử an toàn null trong Java 7 đã được gửi theo Project Coin. Cú pháp hơi khác so với ví dụ trên, nhưng đó là cùng một khái niệm.


Cập nhật: Đề xuất của nhà điều hành an toàn null đã không được đưa vào Project Coin. Vì vậy, bạn sẽ không thấy cú pháp này trong Java 7.


20
Tôi nghĩ rằng điều này là sai. Cần có một cách để xác định rằng một biến đã cho là LUÔN LUÔN không null.
Thorbjørn Ravn Andersen

5
Cập nhật: đề xuất sẽ không tạo Java7. Xem blog.sun.com/darcy/entry/project_coin_final_five .
Boris Terzic

9
Ý tưởng thú vị nhưng sự lựa chọn cú pháp là vô lý; Tôi không muốn một codebase đầy dấu chấm hỏi được gắn vào mọi khớp.
Cướp

7
Toán tử này tồn tại trong Groovy , vì vậy những người muốn sử dụng nó vẫn có tùy chọn đó.
Muhd

8
Đây là ý tưởng khéo léo nhất mà tôi từng thấy. Nó nên được thêm vào mọi ngôn ngữ hợp lý của cú pháp C. Tôi thà "đánh dấu câu hỏi" ở khắp mọi nơi hơn là cuộn qua các dòng hoặc tránh "mệnh đề bảo vệ" cả ngày.
Victor

196

Nếu các giá trị không xác định không được phép:

Bạn có thể định cấu hình IDE của mình để cảnh báo bạn về hội thảo null tiềm năng. Ví dụ: trong Eclipse, xem Tùy chọn> Java> Trình biên dịch> Lỗi / Cảnh báo / Phân tích Null .

Nếu các giá trị không xác định được cho phép:

Nếu bạn muốn xác định API mới trong đó các giá trị không xác định có ý nghĩa , hãy sử dụng Mẫu tùy chọn (có thể quen thuộc với các ngôn ngữ chức năng). Nó có những ưu điểm sau:

  • Nó được nêu rõ ràng trong API cho dù đầu vào hay đầu ra có tồn tại hay không.
  • Trình biên dịch buộc bạn phải xử lý trường hợp "không xác định".
  • Tùy chọn là một đơn nguyên , do đó không cần kiểm tra null dài dòng, chỉ cần sử dụng map / foreach / getOrElse hoặc một tổ hợp tương tự để sử dụng giá trị một cách an toàn (ví dụ) .

Java 8 có một Optionallớp tích hợp (được khuyến nghị); cho các phiên bản trước đó, có những lựa chọn thay thế thư viện, ví dụ ổi 's Optionalhoặc FunctionalJava ' s Option. Nhưng giống như nhiều mẫu kiểu chức năng, sử dụng Tùy chọn trong Java (thậm chí 8) dẫn đến khá nhiều bản tóm tắt, bạn có thể giảm bằng cách sử dụng ngôn ngữ JVM ít dài hơn, ví dụ Scala hoặc Xtend.

Nếu bạn phải xử lý một API có thể trả về null , bạn không thể làm gì nhiều trong Java. Xtend và Groovy có toán tử Elvis ?:toán tử quy ước an toàn null ?. , nhưng lưu ý rằng điều này trả về null trong trường hợp tham chiếu null, vì vậy nó chỉ "trì hoãn" việc xử lý null đúng cách.


22
Thật vậy, mẫu Tùy chọn là tuyệt vời. Một số tương đương Java tồn tại. Quả ổi chứa một phiên bản giới hạn của cái gọi là Tùy chọn, loại bỏ hầu hết các thứ chức năng. Trong Haskell, mô hình này được gọi là Có thể.
Ben Hardy

9
Một lớp tùy chọn sẽ có sẵn trong Java 8
Pierre Henry

1
... và nó không (chưa) có bản đồ cũng như bản đồ: download.java.net/jdk8/docs/api/java/util/Optional.html
thSoft

3
Mẫu tùy chọn không giải quyết được gì; thay vì một đối tượng có khả năng null, bây giờ bạn có hai.
Boann

1
@Boann, nếu được sử dụng cẩn thận, bạn sẽ giải quyết tất cả vấn đề NPE của mình. Nếu không, thì tôi đoán có vấn đề "sử dụng".
Louis F.

189

Chỉ cho tình huống này -

Không kiểm tra xem một biến có phải là null hay không trước khi gọi một phương thức bằng (một ví dụ so sánh chuỗi bên dưới):

if ( foo.equals("bar") ) {
 // ...
}

sẽ dẫn đến một NullPointerExceptionnếu fookhông tồn tại.

Bạn có thể tránh điều đó nếu bạn so sánh Strings của bạn như thế này:

if ( "bar".equals(foo) ) {
 // ...
}

42
Tôi đồng ý - chỉ trong tình huống đó. Tôi không thể đứng lập trình viên đã đưa điều này đến cấp độ không cần thiết tiếp theo và viết nếu (null! = MyVar) ... chỉ trông xấu xí đối với tôi và không phục vụ mục đích!
Alex Worden

20
Đây là một ví dụ cụ thể, có lẽ được sử dụng nhiều nhất, về một thực tiễn tốt chung: nếu bạn biết điều đó, luôn luôn làm <object that you know that is not null>.equals(<object that might be null>);. Nó hoạt động cho các phương thức khác ngoài việc equalsbạn biết hợp đồng và các phương thức đó có thể xử lý nullcác tham số.
Stef

9
Đây là ví dụ đầu tiên tôi thấy về Điều kiện Yoda thực sự có ý nghĩa
Erin Drumond

6
NullPulumExceptions được ném vì một lý do. Chúng bị ném vì một đối tượng là null ở nơi không nên. Đó là công việc lập trình viên để CỐ ĐỊNH điều này, không che giấu vấn đề.
Oliver Watkins

1
Thử-Catch-DoNoth che giấu vấn đề, đây là một cách thực hành hợp lệ để giải quyết vấn đề thiếu đường trong ngôn ngữ.
echox

167

Với Java 8, java.util.Optionallớp mới có thể giải quyết được một số vấn đề. Ít nhất người ta có thể nói rằng nó cải thiện khả năng đọc mã và trong trường hợp API công khai làm cho hợp đồng của API rõ ràng hơn với nhà phát triển ứng dụng khách.

Họ làm việc như thế:

Một đối tượng tùy chọn cho một kiểu đã cho ( Fruit) được tạo như kiểu trả về của một phương thức. Nó có thể trống hoặc chứa một Fruitđối tượng:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Bây giờ hãy xem mã này nơi chúng tôi tìm kiếm danh sách Fruit( fruits) cho một thể hiện Fruit đã cho:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

Bạn có thể sử dụng map()toán tử để thực hiện tính toán trên - hoặc trích xuất một giá trị từ - một đối tượng tùy chọn. orElse()cho phép bạn cung cấp dự phòng cho các giá trị bị thiếu.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

Tất nhiên, việc kiểm tra giá trị null / rỗng vẫn là cần thiết, nhưng ít nhất nhà phát triển nhận thức được rằng giá trị có thể trống và nguy cơ quên kiểm tra là có giới hạn.

Trong một API được xây dựng từ đầu bằng cách sử dụng Optionalbất cứ khi nào giá trị trả về có thể trống và chỉ trả về một đối tượng đơn giản khi không thể null(quy ước), mã máy khách có thể từ bỏ kiểm tra null đối với các giá trị trả về đối tượng đơn giản ...

Tất nhiên Optionalcũng có thể được sử dụng làm đối số phương thức, có lẽ là cách tốt hơn để chỉ ra các đối số tùy chọn hơn 5 hoặc 10 phương thức nạp chồng trong một số trường hợp.

Optionalcung cấp các phương thức tiện lợi khác, như orElsecho phép sử dụng giá trị mặc định và ifPresenthoạt động với các biểu thức lambda .

Tôi mời bạn đọc bài viết này (nguồn chính của tôi để viết câu trả lời này) trong đó giải pháp NullPointerException(và nói chung là con trỏ null) có vấn đề cũng như giải pháp (một phần) do Optionalgiải thích được đưa ra: Các đối tượng tùy chọn Java .


11
Cây ổi của Google có một tùy chọn tùy chọn cho Java 6+.
Bradley Gottfried

13
Điều rất quan trọng là nhấn mạnh rằng chỉ sử dụng Tùy chọn với ifPftime () không thêm nhiều giá trị so với kiểm tra null thông thường. Giá trị cốt lõi của nó là nó là một đơn nguyên có thể được sử dụng trong các chuỗi chức năng của map / flapMap, giúp đạt được kết quả tương tự như toán tử Elvis trong Groovy được đề cập ở nơi khác. Mặc dù không có cách sử dụng này, tôi thấy cú pháp orElse / orElseThrow cũng rất hữu ích.
Cornel Masson

Blog này có một mục tốt trên Winterbe.com/posts/2015/03/15/avoid-null-checks-in-java
JohnC

4
Tại sao mọi người có xu hướng làm điều này if(optional.isPresent()){ optional.get(); }thay vìoptional.ifPresent(o -> { ...})
Satyendra Kumar

1
Vì vậy, ngoài các gợi ý về hợp đồng API, nó thực sự chỉ phục vụ cho các lập trình viên chức năng, những người thích các phương thức xích vô tận.
nghiền nát

125

Tùy thuộc vào loại đối tượng bạn đang kiểm tra, bạn có thể sử dụng một số lớp trong commache commons như: apache commons langapache commons bộ sưu tập

Thí dụ:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

hoặc (tùy thuộc vào những gì bạn cần kiểm tra):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

Lớp StringUtils chỉ là một trong số nhiều; có khá nhiều lớp tốt trong commons thực hiện thao tác an toàn null.

Dưới đây là một ví dụ về cách bạn có thể sử dụng vallidation null trong JAVA khi bạn bao gồm thư viện apache (commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

Và nếu bạn đang sử dụng Spring, Spring cũng có chức năng tương tự trong gói của nó, hãy xem thư viện (spring-2.4.6.jar)

Ví dụ về cách sử dụng lớp tĩnh này từ mùa xuân (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

5
Ngoài ra, bạn có thể sử dụng phiên bản chung hơn từ Apache Commons, khá hữu ích khi bắt đầu các phương thức để kiểm tra các thông số tôi tìm thấy. Validate.notNull (đối tượng, "đối tượng không được rỗng"); commons.apache.org/lang/apidocs/org/apache/commons/lang/iêu
monojohnny

@monojohnny Xác thực sử dụng câu lệnh Khẳng định thành?. tôi yêu cầu bởi vì Assert có thể được kích hoạt / hủy kích hoạt trên JVM và nó đề nghị không sử dụng trong sản xuất.
Kurapika

Đừng nghĩ như vậy - Tôi tin rằng nó chỉ ném RuntimeException nếu xác thực thất bại
monojohnny

96
  • Nếu bạn xem xét một đối tượng không nên là null (hoặc đó là một lỗi), hãy sử dụng một xác nhận.
  • Nếu phương thức của bạn không chấp nhận thông số null, hãy nói nó trong javadoc và sử dụng một xác nhận.

Bạn phải kiểm tra đối tượng! = Null nếu bạn muốn xử lý trường hợp đối tượng có thể là null ...

Có một đề xuất để thêm các chú thích mới trong Java7 để trợ giúp với các thông số null / notnull: http://tech.puredanger.com/java7/#jsr308



91

Tôi là một fan hâm mộ của mã "thất bại nhanh". Hãy tự hỏi mình - bạn có đang làm điều gì đó hữu ích trong trường hợp tham số là null không? Nếu bạn không có câu trả lời rõ ràng cho những gì mã của bạn nên làm trong trường hợp đó ... Tức là nó không bao giờ nên rỗng ở vị trí đầu tiên, sau đó bỏ qua nó và cho phép ném NullPulumException. Mã gọi sẽ có ý nghĩa tương tự như một NPE giống như IllegalArgumentException, nhưng nhà phát triển sẽ dễ dàng gỡ lỗi hơn và hiểu điều gì đã xảy ra nếu NPE bị ném thay vì mã của bạn đang cố thực hiện một số trường hợp bất ngờ khác logic - cuối cùng dẫn đến ứng dụng thất bại.


2
tốt hơn để sử dụng các xác nhận, tức là Contract.notNull (abc, "abc phải là null, có phải nó không tải trong xyz không?"); - đây là một cách nhỏ gọn hơn so với thực hiện if (abc! = null) {ném RuntimeException mới ...}
ianpojman

76

Khung bộ sưu tập của Google cung cấp một cách tốt và thanh lịch để đạt được kiểm tra null.

Có một phương thức trong một lớp thư viện như thế này:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

Và cách sử dụng là (với import static):

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

Hoặc trong ví dụ của bạn:

checkNotNull(someobject).doCalc();

71
Mmm, sự khác biệt là gì? p.getAge () sẽ ném cùng một NPE với ít chi phí hoạt động hơn và dấu vết ngăn xếp rõ ràng hơn. Tôi đang thiếu gì?
mysomic

15
Tốt hơn là ném IllegalArgumentException ("e == null") vào ví dụ của bạn vì nó chỉ rõ rằng đó là ngoại lệ dành cho lập trình viên (cùng với đủ thông tin để thực sự cho phép người bảo trì xác định vấn đề). NullPulumExceptions nên được dành riêng cho JVM, vì sau đó nó chỉ ra rõ ràng rằng điều này là vô ý (và thường xảy ra ở một nơi khó xác định)
Thorbjørn Ravn Andersen

6
Đây hiện là một phần của Google Guava.
Steven Benitez

32
Mùi như quá kỹ thuật với tôi. Chỉ cần để JVM ném NPE và không làm lộn xộn mã của bạn với rác này.
Alex Worden

5
Tôi thích nó và mở hầu hết các phương thức và hàm tạo với các kiểm tra rõ ràng về các đối số; nếu có lỗi, các phương thức luôn bị lỗi ở một vài dòng đầu tiên và tôi biết tham chiếu vi phạm mà không tìm thấy cái gì đó nhưgetThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");
Cory Kendall

75

Đôi khi, bạn có các phương thức hoạt động trên các tham số xác định hoạt động đối xứng:

a.f(b); <-> b.f(a);

Nếu bạn biết b không bao giờ có thể là null, bạn có thể trao đổi nó. Nó là hữu ích nhất cho bằng: Thay vì foo.equals("bar");làm tốt hơn "bar".equals(foo);.


2
Nhưng sau đó bạn phải giả sử equals(có thể là bất kỳ phương thức nào) sẽ xử lý null chính xác. Thực sự tất cả những gì đang làm là chuyển trách nhiệm cho người khác (hoặc phương pháp khác).
Supericy

4
@Supericy Về cơ bản là có, nhưng equals(hoặc bất kỳ phương pháp nào) phải kiểm tra xem nullsao. Hoặc nói rõ rằng nó không.
Fuchs

74

Thay vì Null Object Pattern - có công dụng của nó - bạn có thể xem xét các tình huống trong đó đối tượng null là một lỗi.

Khi ngoại lệ được ném, kiểm tra dấu vết ngăn xếp và xử lý lỗi.


17
Vấn đề là thường thì bạn mất ngữ cảnh vì NullPulumException không chỉ ra biến WHICH là null và bạn có thể có một số thao tác "." - trên dòng. Sử dụng "if (foo == null) ném RuntimeException mới (" foo == null ")" cho phép bạn nêu rõ CÁI GÌ đã sai, cung cấp cho dấu vết ngăn xếp của bạn nhiều giá trị hơn cho những người phải sửa nó.
Thorbjørn Ravn Andersen

1
Với Andersen - tôi rất thích hệ thống ngoại lệ của Java để có thể bao gồm tên của một biến đang được sử dụng, do đó NullPulumExceptions sẽ không chỉ chỉ ra dòng ngoại lệ xảy ra mà còn cả tên biến. Điều này sẽ hoạt động tốt trong phần mềm không được kiểm soát.
fwielstra

Tôi chưa thể làm cho nó hoạt động được, nhưng điều này nhằm giải quyết chính xác vấn đề đó.
MatrixFrog

Vấn đề là gì? Chỉ cần bắt NPE ở mức thích hợp khi có đủ ngữ cảnh, kết xuất thông tin ngữ cảnh và suy nghĩ lại ngoại lệ ... thật dễ dàng với Java.
dùng1050755

3
Tôi đã có một giáo sư đã rao giảng chống lại phương pháp gọi chuỗi. Lý thuyết của ông là bạn nên cảnh giác với các chuỗi cuộc gọi dài hơn 2 phương thức. Tôi không biết nếu đó là một quy tắc cứng, nhưng nó chắc chắn loại bỏ hầu hết các vấn đề với dấu vết ngăn xếp NPE.
RustyTheBoyRobot

74

Null không phải là một "vấn đề". Nó là một phần không thể thiếu của một bộ công cụ mô hình hoàn chỉnh . Phần mềm nhằm mục đích mô hình hóa sự phức tạp của thế giới và null mang gánh nặng của nó. Null chỉ ra 'Không có dữ liệu' hoặc 'Không xác định' trong Java và tương tự. Vì vậy, nó là thích hợp để sử dụng null cho các mục đích này. Tôi không thích mẫu 'Đối tượng Null'; Tôi nghĩ nó làm tăng vấn đề ' ai sẽ bảo vệ những người bảo vệ '.
Nếu bạn hỏi tôi tên bạn gái của tôi là gì thì tôi sẽ nói với bạn rằng tôi không có bạn gái. Trong ngôn ngữ Java, tôi sẽ trả về null. Một cách khác là ném ngoại lệ có ý nghĩa để chỉ ra một số vấn đề không thể (hoặc không '

  1. Đối với một 'câu hỏi chưa biết' hãy đưa ra 'câu trả lời chưa biết'. (Không an toàn khi điều này đúng theo quan điểm kinh doanh) Kiểm tra đối số cho null một lần trong một phương thức trước khi sử dụng làm giảm nhiều người gọi khỏi việc kiểm tra chúng trước khi gọi.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }

    Trước đây dẫn đến luồng logic thông thường để không có ảnh của bạn gái không tồn tại từ thư viện ảnh của tôi.

    getPhotoOfThePerson(me.getGirlfriend())

    Và nó phù hợp với API Java mới sắp ra mắt (mong chờ)

    getPhotoByName(me.getGirlfriend()?.getName())

    Mặc dù không tìm thấy ảnh được lưu trữ trong DB cho một số người, nhưng tôi thường sử dụng các cặp như dưới đây cho một số trường hợp khác

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);

    Và đừng ghét gõ <alt> + <shift> + <j>(tạo javadoc trong Eclipse) và viết thêm ba từ cho API công khai của bạn. Điều này sẽ là quá đủ cho tất cả, trừ những người không đọc tài liệu.

    /**
     * @return photo or null
     */

    hoặc là

    /**
     * @return photo, never null
     */
  2. Đây là trường hợp khá lý thuyết và trong hầu hết các trường hợp, bạn nên sử dụng API an toàn java null (trong trường hợp nó sẽ được phát hành trong 10 năm nữa), nhưng NullPointerExceptionlà lớp con của một Exception. Do đó, đây là một hình thức Throwablechỉ ra các điều kiện mà một ứng dụng hợp lý có thể muốn nắm bắt ( javadoc )! Để sử dụng lợi thế đầu tiên của ngoại lệ và tách mã xử lý lỗi khỏi mã 'thông thường' ( theo người tạo Java ), đối với tôi, cần phải nắm bắt NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }

    Câu hỏi có thể phát sinh:

    Q. Điều gì xảy ra nếu getPhotoDataSource()trả về null?
    A. Đó là tùy thuộc vào logic kinh doanh. Nếu tôi không tìm thấy album ảnh, tôi sẽ không cho xem ảnh. Điều gì xảy ra nếu appContext không được khởi tạo? Logic kinh doanh của phương pháp này đưa ra điều này. Nếu cùng một logic nên nghiêm ngặt hơn thì ném một ngoại lệ, đó là một phần của logic nghiệp vụ và kiểm tra rõ ràng cho null nên được sử dụng (trường hợp 3). Các cơn Java API Null-an toàn mới tốt hơn ở đây để chỉ định cách chọn lọc những gì ngụ ý và những gì không bao hàm được khởi tạo là thất bại nhanh trong trường hợp lỗi lập trình viên.

    Q. Mã dự phòng có thể được thực thi và các tài nguyên không cần thiết có thể được lấy.
    A. Nó có thể diễn ra nếu getPhotoByName()cố gắng mở kết nối cơ sở dữ liệu, tạo PreparedStatementvà sử dụng tên người làm tham số SQL cuối cùng. Cách tiếp cận cho một câu hỏi chưa biết đưa ra một câu trả lời chưa biết (trường hợp 1) hoạt động ở đây. Trước khi lấy tài nguyên, phương thức nên kiểm tra các tham số và trả về kết quả 'không xác định' nếu cần.

    Q. Cách tiếp cận này có một hình phạt hiệu suất do mở đóng cửa thử.
    A. Phần mềm phải dễ hiểu và sửa đổi trước tiên. Chỉ sau này, người ta mới có thể nghĩ về hiệu suất, và chỉ khi cần thiết! và nơi cần thiết! ( nguồn ), và nhiều người khác).

    Tái bút Cách tiếp cận này sẽ hợp lý để sử dụng vì mã xử lý lỗi riêng biệt từ nguyên tắc mã "thông thường" là hợp lý để sử dụng ở một số nơi. Hãy xem xét ví dụ tiếp theo:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }

    PPS. Đối với những người nhanh chóng tải xuống (và không quá nhanh để đọc tài liệu) tôi muốn nói rằng tôi chưa bao giờ bắt gặp một ngoại lệ con trỏ rỗng (NPE) trong đời. Nhưng khả năng này được các nhà sáng tạo Java cố tình thiết kế vì NPE là một lớp con của Exception. Chúng tôi có một tiền lệ trong lịch sử Java khi ThreadDeathlà một Errorkhông phải vì nó thực sự là một lỗi ứng dụng, nhưng chỉ vì nó không được dự định sẽ được bắt! Bao nhiêu NPE phù hợp để trở thành Errorhơn ThreadDeath! Nhưng nó không phải như vậy.

  3. Kiểm tra 'Không có dữ liệu' chỉ khi logic kinh doanh ngụ ý nó.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }

    Nếu appContext hoặc dataSource không được khởi tạo thời gian chạy chưa xử lý, NullPulumException sẽ giết luồng hiện tại và sẽ được xử lý bởi Thread.defaultUncaughtExceptionHandler (để bạn xác định và sử dụng trình ghi nhật ký yêu thích của bạn hoặc cơ chế thông báo khác). Nếu không được đặt, Threadgroup # unsaughtException sẽ in stacktrace thành lỗi hệ thống. Người ta phải theo dõi nhật ký lỗi ứng dụng và mở vấn đề Jira cho từng ngoại lệ chưa được xử lý mà thực tế là lỗi ứng dụng. Lập trình viên nên sửa lỗi ở đâu đó trong công cụ khởi tạo.


6
Bắt NullPointerExceptionvà trở về nulllà khủng khiếp để gỡ lỗi. Dù sao thì bạn cũng kết thúc với NPE sau đó và thật khó để tìm ra cái gì ban đầu là null.
artbristol

23
Tôi sẽ downvote nếu tôi có danh tiếng. Không chỉ là không cần thiết, nó là một lỗ hổng trong hệ thống loại. Việc gán Cây cho Danh sách là lỗi loại vì cây không phải là giá trị của Danh sách loại; theo cùng logic đó, việc gán null sẽ là một lỗi loại vì null không phải là giá trị của loại Object hoặc bất kỳ loại hữu ích nào cho vấn đề đó. Ngay cả người đàn ông đã phát minh ra null cũng coi đó là "sai lầm tỷ đô" của mình. Khái niệm "một giá trị có thể là giá trị của loại T HOẶC không có gì" là loại riêng của nó và nên được thể hiện như vậy (ví dụ: Có thể <T> hoặc Tùy chọn <T>).
Doval

2
Kể từ "Có thể <T> hoặc <T> tùy chọn", bạn vẫn cần viết mã như if (maybeNull.hasValue()) {...}vậy, sự khác biệt là if (maybeNull != null)) {...}gì?
Mykhaylo Adamovych

1
Kể từ khi "bắt NullPulumException và trả về null là điều khủng khiếp để gỡ lỗi. Dù sao thì cuối cùng bạn cũng bị NPE và thật khó để tìm ra cái gì ban đầu là null". Tôi đồng ý hoàn toàn! Trong những trường hợp đó, bạn nên viết hàng tá câu lệnh 'if' hoặc ném NPE nếu logic nghiệp vụ ngụ ý dữ liệu tại chỗ hoặc sử dụng toán tử an toàn null từ Java mới. Nhưng có những trường hợp khi tôi không quan tâm đến bước chính xác nào cho tôi null. Ví dụ: tính toán một số giá trị cho người dùng ngay trước khi hiển thị trên màn hình khi bạn mong đợi dữ liệu có thể bị thiếu.
Mykhaylo Adamovych

3
@MykhayloAdamovych: Lợi ích của Maybe<T>hoặc Optional<T>không phải trong trường hợp bạn Tcó thể là null, nhưng trong trường hợp không bao giờ nên là null. Nếu bạn có một loại có nghĩa rõ ràng là "giá trị này có thể là null - sử dụng một cách thận trọng" và bạn sử dụng và trả lại một loại như vậy một cách nhất quán, thì bất cứ khi nào bạn thấy một Tmã cũ trong mã của mình, bạn có thể cho rằng nó không bao giờ là null. (Tất nhiên, điều này sẽ hữu ích hơn rất nhiều nếu trình biên dịch thực thi.)
cHao

71

Java 7 có một java.util.Objectslớp tiện ích mới mà trên đó có một requireNonNull()phương thức. Tất cả điều này là ném một NullPointerExceptionnếu đối số của nó là null, nhưng nó làm sạch mã một chút. Thí dụ:

Objects.requireNonNull(someObject);
someObject.doCalc();

Phương thức này hữu ích nhất để kiểm tra ngay trước khi gán trong hàm tạo, trong đó mỗi lần sử dụng nó có thể lưu ba dòng mã:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

trở thành

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}

9
Trên thực tế, ví dụ của bạn cấu thành sự phình to mã: dòng đầu tiên là thừa vì NPE sẽ bị ném ở dòng thứ hai. ;-)
dùng1050755

1
Thật. Một ví dụ tốt hơn sẽ là nếu dòng thứ hai là doCalc(someObject).
Stuart Marks

Phụ thuộc. Nếu bạn là tác giả của doCalc (), tôi khuyên bạn nên đặt séc vào cơ thể của phương thức đó (nếu có thể). Và sau đó rất có thể bạn sẽ gọi someObject.someMethod () trong đó một lần nữa không cần phải kiểm tra null. :-)
user1050755

Chà, nếu bạn không phải là tác giả doCalc()và nó sẽ không ném NPE ngay lập tức khi được null, bạn cần kiểm tra null và tự ném NPE. Đó là những gì Objects.requireNonNull()dành cho.
Stuart Marks

7
Đó không chỉ là sự phình to mã. Tốt hơn để kiểm tra phía trước hơn một nửa phương pháp gây ra tác dụng phụ hoặc sử dụng thời gian / không gian.
Rob Grant

51

Cuối cùng, cách duy nhất để giải quyết hoàn toàn vấn đề này là sử dụng một ngôn ngữ lập trình khác:

  • Trong Objective-C, bạn có thể thực hiện tương đương với việc gọi một phương thức trên nilvà hoàn toàn không có gì xảy ra. Điều này làm cho hầu hết các kiểm tra null không cần thiết, nhưng nó có thể khiến lỗi khó chẩn đoán hơn nhiều.
  • Trong Nice , một ngôn ngữ có nguồn gốc Java, có hai phiên bản của tất cả các loại: phiên bản có khả năng không có phiên bản và phiên bản không có giá trị. Bạn chỉ có thể gọi các phương thức trên các loại không null. Các loại có khả năng null có thể được chuyển đổi thành các loại không null thông qua kiểm tra rõ ràng cho null. Điều này giúp dễ dàng hơn nhiều để biết nơi nào cần kiểm tra null và nơi không cần kiểm tra.

Ái chà ... Câu trả lời đúng nhất và bị hạ bệ, công lý ở đâu? Trong Java null luôn là một giá trị hợp lệ. Đó là hệ quả của tất cả mọi thứ là một vật thể - Null là một thứ (tất nhiên chúng ta bỏ qua những người nguyên thủy ở đây nhưng bạn hiểu ý). Cá nhân tôi ủng hộ cách tiếp cận của Nice, mặc dù chúng tôi có thể làm cho nó để các phương thức có thể được gọi trên các loại không thể và thúc đẩy NPE đến các ngoại lệ được kiểm tra. Điều này sẽ phải được thực hiện thông qua một công cụ biên dịch mặc dù nó sẽ phá vỡ tất cả các mã hiện có :(
RèmDog

4
Tôi không quen thuộc với Nice, nhưng Kotlin thực hiện cùng một ý tưởng, có các loại nullable và không null được xây dựng trong hệ thống loại ngôn ngữ. Ngắn gọn hơn nhiều so với Tùy chọn hoặc mẫu đối tượng null.
mtsahakis

38

"Vấn đề" phổ biến trong Java thực sự.

Đầu tiên, suy nghĩ của tôi về điều này:

Tôi cho rằng thật tệ khi "ăn" thứ gì đó khi NULL được thông qua trong đó NULL không phải là giá trị hợp lệ. Nếu bạn không thoát khỏi phương thức với một số lỗi thì điều đó có nghĩa là không có gì sai trong phương thức của bạn, điều đó không đúng. Sau đó, bạn có thể trả về null trong trường hợp này và trong phương thức nhận, bạn lại kiểm tra null và nó không bao giờ kết thúc, và bạn kết thúc bằng "if! = Null", v.v.

Vì vậy, IMHO, null phải là một lỗi nghiêm trọng ngăn cản việc thực hiện thêm (nghĩa là trong đó null không phải là giá trị hợp lệ).

Cách tôi giải quyết vấn đề này là:

Đầu tiên, tôi theo quy ước này:

  1. Tất cả các phương thức / API công khai luôn kiểm tra các đối số của nó là null
  2. Tất cả các phương thức riêng tư không kiểm tra null vì chúng là các phương thức được kiểm soát (chỉ cần chết với ngoại lệ nullpulum trong trường hợp nó không được xử lý ở trên)
  3. Các phương thức khác không kiểm tra null là các phương thức tiện ích. Họ là công khai, nhưng nếu bạn gọi cho họ vì một số lý do, bạn biết những thông số bạn vượt qua. Điều này giống như cố gắng đun sôi nước trong ấm mà không cung cấp nước ...

Và cuối cùng, trong mã, dòng đầu tiên của phương thức công khai như sau:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

Lưu ý rằng addParam () tự trả về, để bạn có thể thêm nhiều tham số để kiểm tra.

Phương thức validate()sẽ ném kiểm tra ValidationExceptionnếu bất kỳ tham số nào là null (được kiểm tra hoặc không được kiểm tra có nhiều vấn đề về thiết kế / mùi vị, nhưng tôi ValidationExceptionđược kiểm tra).

void validate() throws ValidationException;

Thông báo sẽ chứa văn bản sau nếu, ví dụ: "plan" là null:

" Giá trị đối số bất hợp pháp null được gặp cho tham số [kế hoạch] "

Như bạn có thể thấy, giá trị thứ hai trong phương thức addParam () (chuỗi) là cần thiết cho thông điệp người dùng, bởi vì bạn không thể dễ dàng phát hiện tên biến được truyền vào, ngay cả với sự phản chiếu (dù sao không phải là chủ đề của bài đăng này ...).

Và vâng, chúng tôi biết rằng ngoài dòng này, chúng tôi sẽ không còn gặp phải giá trị null nữa nên chúng tôi chỉ cần gọi các phương thức trên các đối tượng đó một cách an toàn.

Bằng cách này, mã sạch, dễ bảo trì và dễ đọc.


3
Chắc chắn rồi. Các ứng dụng chỉ ném lỗi và sự cố có chất lượng cao hơn vì không có nghi ngờ gì khi chúng không hoạt động. Các ứng dụng nuốt lỗi ít nhất một cách duyên dáng nhưng thường không hoạt động theo những cách khó nhận thấy và không được sửa. Và khi vấn đề được chú ý, chúng khó gỡ lỗi hơn nhiều.
Paul Jackson

35

Đặt câu hỏi đó chỉ ra rằng bạn có thể quan tâm đến các chiến lược xử lý lỗi. Kiến trúc sư của nhóm bạn nên quyết định cách làm việc lỗi. Có nhiều hướng khác nhau để làm điều đó:

  1. cho phép Ngoại lệ gợn qua - bắt chúng ở 'vòng lặp chính' hoặc trong một số thói quen quản lý khác.

    • kiểm tra các điều kiện lỗi và xử lý chúng một cách thích hợp

Chắc chắn cũng có một cái nhìn về Lập trình hướng theo khía cạnh - họ có những cách gọn gàng để chèn if( o == null ) handleNull()vào mã byte của bạn.


35

Ngoài việc sử dụng, assertbạn có thể sử dụng như sau:

if (someobject == null) {
    // Handle null here then move on.
}

Điều này là tốt hơn một chút so với:

if (someobject != null) {
    .....
    .....



    .....
}

2
Mh, tại sao vậy? Xin đừng cảm thấy bất kỳ sự phòng thủ nào, tôi chỉ muốn tìm hiểu thêm về Java :)
Matthias Meid

9
@Mudu Theo quy tắc chung, tôi thích biểu thức trong câu lệnh if là câu lệnh "tích cực" hơn là câu "phủ định". Vì vậy, nếu tôi thấy if (!something) { x(); } else { y(); }tôi sẽ có xu hướng cấu trúc lại nó if (something) { y(); } else { x(); }(mặc dù người ta có thể lập luận rằng đó != nulllà lựa chọn tích cực hơn ...). Nhưng quan trọng hơn, phần quan trọng của mã không được bọc bên trong {}s và bạn có một mức thụt lề ít hơn cho hầu hết các phương thức. Tôi không biết đó có phải là lý do của fastcodejava không nhưng đó sẽ là của tôi.
MatrixFrog

1
Đây là những gì tôi có xu hướng làm tốt .. Giữ mã sạch trong tôi.
Koray Tugay

34

Đừng bao giờ sử dụng null. Đừng cho phép nó.

Trong các lớp của tôi, hầu hết các trường và biến cục bộ đều có các giá trị mặc định không null và tôi thêm các câu lệnh hợp đồng (xác nhận luôn luôn) ở mọi nơi trong mã để đảm bảo rằng điều này được thi hành (vì nó ngắn gọn hơn và biểu cảm hơn là để nó xuất hiện dưới dạng NPE và sau đó phải giải quyết số dòng, v.v.).

Khi tôi áp dụng thực hành này, tôi nhận thấy rằng các vấn đề dường như tự khắc phục. Bạn sẽ nắm bắt mọi thứ sớm hơn trong quá trình phát triển chỉ vì tình cờ và nhận ra rằng bạn có một điểm yếu .. và quan trọng hơn .. nó giúp gói gọn các mối quan tâm của các mô-đun khác nhau, các mô-đun khác nhau có thể 'tin tưởng' lẫn nhau và không làm vấy bẩn mã với các if = null elsecấu trúc!

Đây là chương trình phòng thủ và kết quả là mã sạch hơn nhiều trong thời gian dài. Luôn vệ sinh dữ liệu, ví dụ ở đây bằng cách thực thi các tiêu chuẩn cứng nhắc và các vấn đề sẽ biến mất.

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

Các hợp đồng giống như các thử nghiệm đơn vị nhỏ luôn chạy, ngay cả trong sản xuất và khi mọi thứ thất bại, bạn biết tại sao, thay vì một NPE ngẫu nhiên, bạn phải tìm ra cách nào đó.


Tại sao điều này sẽ bị hạ cấp? theo kinh nghiệm của tôi, điều này vượt trội hơn nhiều so với các cách tiếp cận khác, rất muốn biết tại sao không
ianpojman

Tôi đồng ý, cách tiếp cận này ngăn chặn các vấn đề liên quan đến null, thay vì sửa chúng bằng cách làm hỏng mã kiểm tra null ở mọi nơi.
ChrisBlom

7
Vấn đề với cách tiếp cận này là nếu tên không bao giờ được đặt, nó có giá trị "<unknown>", hoạt động giống như một giá trị được đặt. Bây giờ hãy nói rằng tôi cần kiểm tra xem tên chưa bao giờ được đặt (chưa biết), tôi phải thực hiện so sánh chuỗi với giá trị đặc biệt "<unknown>".
Steve Kuo

1
Đúng là điểm tốt Steve. Những gì tôi thường làm là có giá trị đó là một hằng số, ví dụ: chuỗi kết thúc tĩnh công khai UNSET = "__ unset" ... private String field = UNSET ... sau đó private boolean isset () {return UNSET.equals (trường); }
ianpojman

IMHO, Đây là một triển khai Mẫu đối tượng Null với chính bạn thực hiện Tùy chọn (Hợp đồng). Làm thế nào nó cư xử trên lớp lớp kiên trì? Tôi không thấy áp dụng trong trường hợp đó.
Kurapika

31

Guava, một thư viện lõi rất hữu ích của Google, có một API đẹp và hữu ích để tránh null. Tôi thấy việc sử dụngAndAvoidingNullExplained rất hữu ích.

Như đã giải thích trong wiki:

Optional<T>là một cách để thay thế một tham chiếu T nullable bằng một giá trị khác null. Tùy chọn có thể chứa tham chiếu T không null (trong trường hợp chúng tôi nói tham chiếu là "hiện tại") hoặc có thể không chứa gì (trong trường hợp đó chúng tôi nói tham chiếu là "vắng mặt"). Nó không bao giờ được nói là "chứa null."

Sử dụng:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

@CodyGuldner Phải, Cody. Tôi đã cung cấp một trích dẫn có liên quan từ các liên kết để cung cấp thêm bối cảnh.
Murat Derya Özen

25

Đây là một vấn đề rất phổ biến đối với mọi nhà phát triển Java. Vì vậy, có hỗ trợ chính thức trong Java 8 để giải quyết các vấn đề này mà không bị lộn xộn mã.

Java 8 đã giới thiệu java.util.Optional<T>. Nó là một thùng chứa có thể có hoặc không giữ giá trị khác null. Java 8 đã đưa ra một cách an toàn hơn để xử lý một đối tượng có giá trị có thể là null trong một số trường hợp. Nó được lấy cảm hứng từ những ý tưởng của HaskellScala .

Tóm lại, lớp Tùy chọn bao gồm các phương thức để xử lý rõ ràng các trường hợp có giá trị hoặc không có. Tuy nhiên, lợi thế so với tham chiếu null là lớp <T> tùy chọn buộc bạn phải suy nghĩ về trường hợp khi không có giá trị. Kết quả là, bạn có thể ngăn chặn các ngoại lệ con trỏ null ngoài ý muốn.

Trong ví dụ trên, chúng tôi có một nhà máy dịch vụ gia đình trả lại tay cầm cho nhiều thiết bị có sẵn trong nhà. Nhưng các dịch vụ này có thể có hoặc không có sẵn / chức năng; điều đó có nghĩa là nó có thể dẫn đến một NullPulumException. Thay vì thêm nullif điều kiện trước khi sử dụng bất kỳ dịch vụ nào, hãy bọc nó vào <Service> tùy chọn.

VIẾT ĐỂ LỰA CHỌN <T>

Hãy xem xét một phương pháp để có được một tài liệu tham khảo về một dịch vụ từ một nhà máy. Thay vì trả về tham chiếu dịch vụ, hãy bọc nó bằng Tùy chọn. Nó cho phép người dùng API biết rằng dịch vụ được trả về có thể có hoặc không có sẵn / chức năng, sử dụng phòng thủ

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

Như bạn thấy Optional.ofNullable()cung cấp một cách dễ dàng để có được tài liệu tham khảo được bao bọc. Có một số cách khác để có được tài liệu tham khảo Tùy chọn, Optional.empty()&Optional.of() . Một để trả về một đối tượng trống thay vì trả về null và đối tượng khác để bọc một đối tượng không null tương ứng.

VẬY CHÍNH XÁC NÀO CHÍNH XÁC NÓ ĐỂ TRÁNH KIỂM TRA NULL?

Khi bạn đã bọc một đối tượng tham chiếu, Tùy chọn cung cấp nhiều phương thức hữu ích để gọi các phương thức trên tham chiếu được bao bọc mà không có NPE.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Tùy chọn.ifPftime gọi Người tiêu dùng đã cho bằng một tham chiếu nếu đó là giá trị khác không. Nếu không, nó không làm gì cả.

@FunctionalInterface
public interface Consumer<T>

Đại diện cho một hoạt động chấp nhận một đối số đầu vào duy nhất và không trả lại kết quả. Không giống như hầu hết các giao diện chức năng khác, Người tiêu dùng dự kiến ​​sẽ hoạt động thông qua các tác dụng phụ. Nó rất sạch sẽ và dễ hiểu. Trong ví dụ mã trên,HomeService.switchOn(Service) được gọi nếu tham chiếu giữ tùy chọn là không rỗng.

Chúng tôi thường xuyên sử dụng toán tử ternary để kiểm tra điều kiện null và trả về giá trị thay thế hoặc giá trị mặc định. Tùy chọn cung cấp một cách khác để xử lý tình trạng tương tự mà không cần kiểm tra null. Tùy chọn.orElse (defaultObj) trả về defaultObj nếu Tùy chọn có giá trị null. Hãy sử dụng điều này trong mã mẫu của chúng tôi:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

Bây giờ HomeService.get () làm điều tương tự, nhưng theo cách tốt hơn. Nó kiểm tra xem dịch vụ đã được khởi tạo chưa. Nếu đó là trả lại tương tự hoặc tạo một dịch vụ mới. <T> .orElse (T) tùy chọn giúp trả về giá trị mặc định.

Cuối cùng, đây là NPE của chúng tôi cũng như mã không kiểm tra null:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

Bài viết hoàn chỉnh là NPE cũng như mã Null kiểm tra miễn phí Thực sự? .


Có một kiểm tra null trong mã trên - if(homeServices != null) {có thể được thay đổi thành homeServices.ifPresent(h -> //action);
KrishPrabakar

23

Tôi thích bài viết từ Nat Pryce. Dưới đây là các liên kết:

Trong các bài viết cũng có một liên kết đến kho lưu trữ Git cho Loại có thể Java mà tôi thấy thú vị, nhưng tôi không nghĩ rằng một mình nó có thể làm giảm sự phình mã kiểm tra. Sau khi thực hiện một số nghiên cứu trên Internet, tôi nghĩ ! = Nổnull có thể được giảm chủ yếu bằng thiết kế cẩn thận.


Michael Feathers đã viết một đoạn văn ngắn và thú vị về các cách tiếp cận như cách bạn đã đề cập: manuelp.newsblur.com/site/424
ivan.aguirre

21

Tôi đã thử NullObjectPatternnhưng đối với tôi không phải lúc nào cũng là cách tốt nhất. Đôi khi, một "không hành động" không được chiếm đoạt.

NullPointerExceptionlà một ngoại lệ Thời gian chạy có nghĩa là lỗi của nhà phát triển và với đủ kinh nghiệm, nó sẽ cho bạn biết chính xác lỗi đó ở đâu.

Bây giờ đến câu trả lời:

Cố gắng làm cho tất cả các thuộc tính của bạn và các bộ truy cập của nó ở chế độ riêng tư nhất có thể hoặc tránh để lộ chúng cho khách hàng. Tất nhiên, bạn có thể có các giá trị đối số trong hàm tạo, nhưng bằng cách giảm phạm vi bạn không để lớp máy khách vượt qua một giá trị không hợp lệ. Nếu bạn cần sửa đổi các giá trị, bạn luôn có thể tạo mới object. Bạn chỉ kiểm tra các giá trị trong hàm tạo một lần và trong các phương thức còn lại, bạn có thể gần như chắc chắn rằng các giá trị không phải là null.

Tất nhiên, kinh nghiệm là cách tốt hơn để hiểu và áp dụng đề xuất này.

Byte!


19

Có lẽ cách thay thế tốt nhất cho Java 8 hoặc mới hơn là sử dụng Optionallớp.

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

Điều này đặc biệt hữu ích cho các chuỗi dài các giá trị null có thể. Thí dụ:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

Ví dụ về cách ném ngoại lệ vào null:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 đã giới thiệu Objects.requireNonNullphương thức có thể hữu ích khi cần kiểm tra một cái gì đó không có giá trị. Thí dụ:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

17

Tôi có thể trả lời chung chung hơn!

Chúng ta thường phải đối mặt với vấn đề này khi các phương thức nhận được các tham số theo cách chúng ta không mong đợi (cuộc gọi phương thức xấu là lỗi của lập trình viên). Ví dụ: bạn mong muốn có được một đối tượng, thay vào đó bạn nhận được null. Bạn mong muốn nhận được một Chuỗi có ít nhất một ký tự, thay vào đó bạn nhận được một Chuỗi trống ...

Vì vậy, không có sự khác biệt giữa:

if(object == null){
   //you called my method badly!

}

hoặc là

if(str.length() == 0){
   //you called my method badly again!
}

Cả hai đều muốn đảm bảo rằng chúng tôi đã nhận được các tham số hợp lệ, trước khi chúng tôi thực hiện bất kỳ chức năng nào khác.

Như đã đề cập trong một số câu trả lời khác, để tránh các vấn đề trên, bạn có thể thực hiện theo Thiết kế theo mẫu hợp đồng . Vui lòng xem http://en.wikipedia.org/wiki/Design_by_contract .

Để triển khai mẫu này trong java, bạn có thể sử dụng các chú thích java cốt lõi như javax.annotation.NotNull hoặc sử dụng các thư viện tinh vi hơn như Trình xác thực Hibernate .

Chỉ là một mẫu:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

Bây giờ bạn có thể phát triển chức năng cốt lõi của phương thức của mình một cách an toàn mà không cần kiểm tra các tham số đầu vào, chúng bảo vệ các phương thức của bạn khỏi các tham số không mong muốn.

Bạn có thể tiến thêm một bước và đảm bảo rằng chỉ các pojos hợp lệ mới có thể được tạo trong ứng dụng của bạn. (mẫu từ trang web trình xác nhận ngủ đông)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}

javaxtheo định nghĩa, không phải là "Java lõi".
Tomas

17

Tôi rất coi thường các câu trả lời đề nghị sử dụng các đối tượng null trong mọi tình huống. Mô hình này có thể phá vỡ hợp đồng và chôn vùi các vấn đề ngày càng sâu hơn thay vì giải quyết chúng, không đề cập đến việc sử dụng không đúng cách sẽ tạo ra một đống mã nồi hơi khác sẽ cần bảo trì trong tương lai.

Trong thực tế nếu một cái gì đó được trả về từ một phương thức có thể là null và mã gọi phải đưa ra quyết định về điều đó, thì nên có một cuộc gọi sớm hơn để đảm bảo trạng thái.

Cũng nên nhớ rằng, mẫu đối tượng null sẽ bị đói bộ nhớ nếu được sử dụng mà không cần quan tâm. Đối với điều này - ví dụ của NullObject nên được chia sẻ giữa các chủ sở hữu và không phải là một trường hợp không phù hợp cho từng trường hợp này.

Ngoài ra, tôi không khuyên bạn nên sử dụng mẫu này trong đó loại có nghĩa là biểu diễn kiểu nguyên thủy - giống như các thực thể toán học, không phải là vô hướng: vectơ, ma trận, số phức và các đối tượng POD (Plain Old Data), có nghĩa là giữ trạng thái ở dạng các kiểu dựng sẵn Java. Trong trường hợp sau, bạn sẽ kết thúc việc gọi các phương thức getter với kết quả tùy ý. Ví dụ, phương thức NullPerson.getName () nên trả về cái gì?

Thật đáng để xem xét những trường hợp như vậy để tránh kết quả vô lý.


Giải pháp với "hasBackground ()" có một nhược điểm - nó không an toàn cho chuỗi. Nếu bạn cần gọi hai phương thức thay vì một, bạn cần đồng bộ hóa toàn bộ chuỗi trong môi trường đa luồng.
pkalinow

@pkalinow Bạn đã làm một ví dụ giả định chỉ để chỉ ra rằng giải pháp này có một nhược điểm. Nếu mã không có nghĩa là chạy trong ứng dụng đa luồng thì không có nhược điểm nào. Tôi có thể đặt cho bạn 90% mã của bạn không phải là luồng an toàn. Chúng tôi không nói ở đây về khía cạnh mã này, chúng tôi đang nói về mẫu thiết kế. Và đa luồng là một chủ đề của riêng nó.
luke1985

Tất nhiên trong một ứng dụng đơn luồng thì không vấn đề gì. Tôi đã đưa ra nhận xét đó bởi vì đôi khi nó là một vấn đề.
pkalinow

@pkalinow Nếu bạn nghiên cứu chủ đề này gần hơn, bạn sẽ thấy rằng mẫu thiết kế Null Object sẽ không khắc phục được các vấn đề đa luồng. Vì vậy, nó không liên quan. Và thành thật mà nói, tôi đã tìm thấy những nơi mà mô hình này sẽ phù hợp một cách độc đáo, vì vậy thực sự câu trả lời ban đầu của tôi là một chút sai.
luke1985

16
  1. Không bao giờ khởi tạo biến thành null.
  2. Nếu (1) là không thể, hãy khởi tạo tất cả các tập hợp và mảng thành các tập hợp / mảng trống.

Làm điều này trong mã của riêng bạn và bạn có thể tránh! = Null kiểm tra.

Hầu hết thời gian kiểm tra null dường như bảo vệ các vòng lặp trên các bộ sưu tập hoặc mảng, vì vậy chỉ cần khởi tạo chúng trống, bạn sẽ không cần bất kỳ kiểm tra null nào.

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

Có một chi phí rất nhỏ trong việc này, nhưng nó đáng giá cho mã sạch hơn và ít NullPulumExceptions.



3
+1 Điều này tôi đồng ý với. Bạn không bao giờ nên trả lại một nửa các đối tượng khởi tạo. Mã liên quan đến Jaxb và mã đậu là notiroius cho việc này. Đó là thực hành xấu. Tất cả các bộ sưu tập nên được khởi tạo và tất cả các đối tượng nên tồn tại với (lý tưởng) không có tham chiếu null. Hãy xem xét một đối tượng có một bộ sưu tập trong đó. Kiểm tra xem đối tượng không phải là null, rằng bộ sưu tập không phải là null và bộ sưu tập không chứa các đối tượng null là không hợp lý và ngu ngốc.
ggb667

15

Đây là lỗi phổ biến nhất xảy ra đối với hầu hết các nhà phát triển.

Chúng tôi có nhiều cách để xử lý việc này.

Cách tiếp cận 1:

org.apache.commons.lang.Validate //using apache framework

notNull (Đối tượng đối tượng, thông báo chuỗi)

Cách tiếp cận 2:

if(someObject!=null){ // simply checking against null
}

Cách tiếp cận 3:

@isNull @Nullable  // using annotation based validation

Cách tiếp cận 4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}

Quảng cáo. 4. Nó không hữu ích lắm - khi bạn kiểm tra xem một con trỏ có rỗng không, bạn có thể muốn gọi một phương thức trên nó. Gọi một phương thức trên null cung cấp cho bạn hành vi tương tự - NullPulumException.
pkalinow

11
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}

2
Có gì sai với phương pháp này, tôi nghĩ rằng @tltester chỉ muốn đưa ra một giá trị mặc định nếu nó là null, điều này có ý nghĩa.
Sawyer

1
Có một phương thức như vậy trong Apache commons-lang : ObjectUtils.defaultIfNull(). Có một điểm chung nữa : ObjectUtils.firstNonNull(), có thể được sử dụng để thực hiện chiến lược xuống cấp:firstNonNull(bestChoice, secondBest, thirdBest, fallBack);
ngà
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.