IllegalArgumentException hoặc NullPulumException cho một tham số null? [đóng cửa]


547

Tôi có một phương thức setter đơn giản cho một thuộc tính và nullkhông phù hợp với thuộc tính cụ thể này. Tôi đã luôn luôn bị giằng xé trong tình huống này: tôi nên ném IllegalArgumentException, hay a NullPointerException? Từ javadocs, cả hai có vẻ thích hợp. Có một số loại tiêu chuẩn được hiểu? Hay đây chỉ là một trong những điều mà bạn nên làm bất cứ điều gì bạn thích và cả hai đều thực sự chính xác?

Câu trả lời:


299

Có vẻ như một lệnh IllegalArgumentExceptionđược gọi nếu bạn không muốn nulltrở thành một giá trị được phép và NullPointerExceptionnó sẽ bị ném nếu bạn đang cố gắng sử dụng một biến hóa ra null.


33
Các câu trả lời sau đây cho câu hỏi này đưa ra lập luận thuyết phục rằng NullPulumException là ngoại lệ chính xác: stackoverflow.com/a/8160/372926 ; stackoverflow.com/a/8196334/372926 ; và stackoverflow.com/a/6353/372926 .
SamStephens

2
IllegalArgumentExceptionmâu thuẫn với các đối tượng của Java.requireNonNull (T) và Guava's Preconditions.checkNotNull (T) ném a NullPointerException. Tuy nhiên, câu trả lời đúng là dứt khoát IllegalArgumentException như được giải thích trong câu trả lời xuất sắc của Jason Cohen và đó là phần bình luận .
matoni

417

Bạn nên sử dụng IllegalArgumentException(IAE), không phải NullPointerException(NPE) vì những lý do sau:

Đầu tiên, NPE JavaDoc liệt kê rõ ràng các trường hợp NPE phù hợp. Lưu ý rằng tất cả chúng được ném bởi bộ thực thi khi nullđược sử dụng không phù hợp. Ngược lại, IAE JavaDoc không thể rõ ràng hơn: "Ném để chỉ ra rằng một phương thức đã được thông qua một đối số bất hợp pháp hoặc không phù hợp." Yup, đó là bạn!

Thứ hai, khi bạn thấy một NPE trong dấu vết ngăn xếp, bạn giả định điều gì? Có lẽ là ai đó đã bỏ qua a null. Khi bạn thấy IAE, bạn giả sử người gọi phương thức ở đầu ngăn xếp được truyền vào một giá trị bất hợp pháp. Một lần nữa, giả định sau là đúng, trước đây là sai lệch.

Thứ ba, vì IAE được thiết kế rõ ràng để xác thực các tham số, bạn phải coi đó là lựa chọn ngoại lệ mặc định, vậy tại sao bạn lại chọn NPE thay thế? Chắc chắn không phải đối với các hành vi khác nhau - bạn có thực sự mong đợi việc gọi mã để bắt NPE tách biệt với IAE và kết quả là làm một cái gì đó khác đi không? Bạn đang cố gắng để truyền đạt một thông báo lỗi cụ thể hơn? Nhưng dù sao bạn cũng có thể làm điều đó trong văn bản thông báo ngoại lệ, như bạn nên làm cho tất cả các tham số không chính xác khác.

Thứ tư, tất cả các dữ liệu tham số không chính xác khác sẽ là IAE, vậy tại sao không nhất quán? Tại sao một điều bất hợp pháp nulllại đặc biệt đến nỗi nó xứng đáng có một ngoại lệ riêng biệt với tất cả các loại lập luận bất hợp pháp khác?

Cuối cùng, tôi chấp nhận đối số được đưa ra bởi các câu trả lời khác rằng các phần của API Java sử dụng NPE theo cách này. Tuy nhiên, API Java không phù hợp với tất cả mọi thứ, từ các loại ngoại lệ đến các quy ước đặt tên, vì vậy tôi nghĩ chỉ cần sao chép một cách mù quáng (phần yêu thích của bạn) API Java không phải là một đối số đủ tốt để vượt qua những cân nhắc khác này.


119
Phiên bản Java thứ 2 hiệu quả, Mục 60: "Có thể cho rằng, tất cả các yêu cầu phương thức sai lầm đều dẫn đến một đối số bất hợp pháp hoặc trạng thái bất hợp pháp, nhưng các trường hợp ngoại lệ khác được sử dụng tiêu chuẩn cho một số loại đối số và trạng thái bất hợp pháp. Các giá trị null bị cấm, quy ước chỉ ra rằng NullPulumException sẽ bị ném chứ không phải IllegalArgumentException. Tương tự, nếu một người gọi chuyển một giá trị ngoài phạm vi trong một tham số thể hiện một chỉ mục vào một chuỗi, IndexOutOfBoundException sẽ được ném thay vì IllegalArgument.
Gili

33
JavaDoc cho NPE cũng nêu rõ như sau: "Các ứng dụng nên ném các thể hiện của lớp này để chỉ ra việc sử dụng bất hợp pháp khác của đối tượng null." Điều này có thể rõ ràng hơn :(
R. Martinho Fernandes

12
Thật không may, cả hai phương thức xác nhận Validate.notNull(commons lang) và Preconditions.checkNotNull(ổi) đều ném NPE :-(
Aaron Digulla

2
Mặc dù Guava cũng có Preconditions.checkArgument () ném IllegalArgumentException ...
michaelok 17/12/13

16
@AaronDigulla, từ các tài liệu của Guava: "Chúng tôi nhận thấy có nhiều lý lẽ hợp lệ ủng hộ việc ném IAE vào một cuộc tranh cãi vô giá trị. Thực tế, nếu chúng ta có một cỗ máy thời gian quay trở lại> 15 năm, chúng ta thậm chí có thể cố gắng đẩy mọi thứ vào Tuy nhiên, chúng tôi đã quyết định gắn bó với ưu tiên JDK và Java hiệu quả của NullPulumException. Nếu bạn kiên định với niềm tin rằng IAE đúng, bạn vẫn có checkArgument(arg != null), chỉ cần không có sự tiện lợi của nó, hoặc bạn có thể tạo ra một tiện ích cục bộ cho dự án của bạn. " code.google.com/p/guava-lologists/wiki/IdeaGraveyard
Arto Bendiken

162

Tiêu chuẩn là ném NullPointerException. "Java hiệu quả" thường không thể thảo luận về điều này một cách ngắn gọn trong Mục 42 (phiên bản đầu tiên), Mục 60 (phiên bản thứ hai) hoặc Mục 72 (phiên bản thứ ba) "Ưu tiên sử dụng các ngoại lệ tiêu chuẩn":

"Có thể cho rằng, tất cả các yêu cầu phương pháp sai lầm đều dẫn đến một đối số bất hợp pháp hoặc trạng thái bất hợp pháp, nhưng các trường hợp ngoại lệ khác được sử dụng tiêu chuẩn cho một số loại đối số và trạng thái bất hợp pháp. Nếu một người gọi chuyển thành null trong một số tham số mà giá trị null bị cấm, quy ước sẽ ra lệnh NullPulumException được ném chứ không phải IllegalArgumentException. "


95
Tôi rất không đồng ý. Chỉ nên ném NullPulumExceptions nếu JVM tình cờ tuân theo tham chiếu null. Đó là sự khác biệt giúp bạn khi được gọi để xem mã vào lúc 3 giờ sáng.
Thorbjørn Ravn Andersen

26
Tôi không nhất thiết phải đồng ý với tiêu chuẩn (tôi thực sự có thể đi vào vấn đề này), nhưng đó là cách sử dụng tiêu chuẩn trong suốt JDK, do đó Java có hiệu quả trong trường hợp đó. Tôi nghĩ rằng đây là một trường hợp lựa chọn có tuân theo tiêu chuẩn hay không, hoặc làm điều bạn cảm thấy là đúng. Trừ khi bạn có một lý do rất chính đáng (và điều này chắc chắn có thể đủ điều kiện), tốt nhất là bạn nên tuân theo tiêu chuẩn thực hành.
GaryF

21
Dấu vết ngoại lệ cho thấy điểm của ngoại lệ, vì vậy nếu sự khác biệt về loại ngoại lệ gây ra địa ngục cho bạn hoặc là "sự khác biệt giúp bạn", thì bạn đang làm điều gì đó rất sai.
Jim Balter

8
Tưởng tượng và ngụy biện. Ngay bên dưới, MB viết "Lưu ý rằng các đối số về gỡ lỗi cứng là không có thật vì tất nhiên bạn có thể cung cấp một thông báo cho NullPulumException nói điều gì là null và tại sao nó không nên null. Giống như với IllegalArgumentException."
Jim Balter

10
Là người trả lời ban đầu ở đây, hãy để tôi nói rằng nó không quan trọng lắm. Nó chắc chắn không đảm bảo 6 năm nói chuyện. Chọn một, bất cứ điều gì bạn thích, và nhất quán. Tiêu chuẩn, như tôi đã chỉ ra, là NPE. Nếu bạn thích IAE vì bất cứ lý do gì, hãy tìm nó. Chỉ cần kiên định.
GaryF

138

Tôi đã hết sức ủng hộ việc ném IllegalArgumentExceptioncác tham số null, cho đến ngày hôm nay, khi tôi nhận thấy java.util.Objects.requireNonNullphương thức trong Java 7. Với phương thức đó, thay vì thực hiện:

if (param == null) {
    throw new IllegalArgumentException("param cannot be null.");
}

bạn có thể làm:

Objects.requireNonNull(param);

và nó sẽ ném NullPointerExceptionnếu tham số bạn vượt qua null.

Cho rằng phương pháp đó là đúng ở giữa java.utiltôi coi sự tồn tại của nó là một dấu hiệu khá mạnh mẽ rằng ném NullPointerExceptionlà "cách làm việc của Java".

Tôi nghĩ rằng tôi đã quyết định ở mức nào.

Lưu ý rằng các đối số về gỡ lỗi cứng là không có thật vì tất nhiên bạn có thể cung cấp một thông báo để NullPointerExceptionnói điều gì là null và tại sao nó không nên là null. Cũng giống như với IllegalArgumentException.

Một ưu điểm nữa NullPointerExceptionlà, trong mã quan trọng hiệu năng cao, bạn có thể phân phối bằng một kiểm tra rõ ràng về null (và một NullPointerExceptionthông báo lỗi thân thiện) và chỉ cần dựa vào NullPointerExceptionbạn sẽ tự động nhận được khi bạn gọi một phương thức trên null tham số. Miễn là bạn gọi một phương thức nhanh chóng (tức là thất bại nhanh), thì về cơ bản bạn cũng có tác dụng tương tự, chỉ là không thân thiện với người dùng cho nhà phát triển. Hầu hết các lần kiểm tra rõ ràng và ném một thông điệp hữu ích để chỉ ra tham số nào là null, nhưng thật tuyệt khi có tùy chọn thay đổi nếu hiệu suất ra lệnh mà không phá vỡ hợp đồng đã xuất bản của phương thức / hàm tạo.


14
Quả ổi Preconditions.checkNotNull(arg)cũng ném NPE.
assylias

14
Điều này thực sự không tăng thêm trọng lượng cho NullPulumException cho các ARGUMENT null bất hợp pháp. Cả JDK requestNonNull () và Guava checkNotNull () có thể được gọi ở bất kỳ đâu trong mã, với bất kỳ đối tượng nào. Bạn có thể gọi chúng trong một vòng lặp chẳng hạn. Yêu cầuNotNull () và checkNotNull () không thể giả sử được gọi với một số đối số phương thức và ném IllegalArgumentException. Lưu ý rằng Guava cũng có Preconditions.checkArgument () không ném IllegalArgumentException.
Bogdan Calmac

2
Một điểm công bằng của Bogdan, mặc dù tôi nghi ngờ việc sử dụng điển hình (và thường được dự định) của allowNonNull là để kiểm tra đối số. Nếu bạn cần kiểm tra xem có thứ gì đó không có giá trị trong một vòng lặp tôi đã nghĩ rằng một khẳng định sẽ là cách điển hình.
MB.

15
Tôi nghĩ rằng JavaDoc của Object.requireNonNull () làm tăng thêm sức nặng cho đối số: "Phương thức này được thiết kế chủ yếu để thực hiện xác thực tham số trong các phương thức và hàm tạo"
Richard Zschech 2/215

Hãy nhớ rằng đối số hiệu suất có lợi cho kiểm tra null ẩn thường không hợp lệ. JIT có thể nhận ra các kiểm tra null của người dùng và bỏ qua kiểm tra null ngụ ý sau đây và / hoặc sử dụng cùng một bộ tối ưu hóa trên cả hai loại kiểm tra null. Xem wiki này để biết thêm thông tin, cụ thể: Kiểm tra null do người dùng viết trong hầu hết các trường hợp giống hệt về mặt chức năng với các kiểm tra được chèn bởi JVM. Tất nhiên, nếu bạn đang làm một cái gì đó lạ hơn trong tin nhắn tùy chỉnh của mình, thì việc tối ưu hóa này có thể không được áp dụng.
BeeOnRope

70

Tôi có xu hướng tuân theo thiết kế của các thư viện JDK, đặc biệt là Bộ sưu tập và Đồng thời (Joshua Bloch, Doug Lea, những người đó biết cách thiết kế API vững chắc). Dù sao, nhiều API trong JDK chủ động ném NullPointerException.

Ví dụ: Javadoc cho Map.containsKeycác trạng thái:

@throws NullPulumException nếu khóa là null và bản đồ này không cho phép các khóa null (tùy chọn).

Hoàn toàn hợp lệ để ném NPE của riêng bạn. Quy ước là bao gồm tên tham số là null trong thông báo của ngoại lệ.

Mô hình đi:

public void someMethod(Object mustNotBeNull) {  
    if (mustNotBeNull == null) {  
        throw new NullPointerException("mustNotBeNull must not be null");  
    }  
}

Dù bạn làm gì, đừng cho phép một giá trị xấu được thiết lập và ném ngoại lệ sau đó khi các mã khác cố gắng sử dụng nó. Điều đó làm cho việc gỡ lỗi trở thành một cơn ác mộng. Bạn phải luôn tuân theo nguyên tắc "thất bại nhanh".


3
Thực phẩm cho suy nghĩ: Có thể lý do mà NullPulumException không mở rộng IllegalArgumentException là vì điều này có thể xảy ra trong các trường hợp không liên quan đến các đối số phương thức.
Gili

2
@Gili: có thể vấn đề này tồn tại ngay từ đầu vì Java không hỗ trợ nhiều kế thừa. Nếu Java hỗ trợ MI, bạn có thể đưa ra một ngoại lệ kế thừa từ cả IllegalArgumentException và NullPulumException.
Lie Ryan

7
Thông điệp không cần bao gồm đối số, vì nó sẽ luôn là null, đưa ra: "null không được null", không hữu ích lắm. :-) Nếu không, tôi đồng ý, bạn có thể tạo một NPE "phong phú", với một thông điệp có ý nghĩa.
PhiLho

5
Tôi phải đồng ý - tuân theo API tiêu chuẩn khi nghi ngờ. Không phải tất cả mọi thứ trong API đều là ý tưởng tối ưu của bạn, nhưng nó vẫn được duy trì và lặp lại bởi hàng trăm nhà phát triển và được sử dụng bởi hàng triệu lập trình viên. Bây giờ trong Java 7, chúng ta có một ví dụ khác về NPE đang được sử dụng theo cách này; phương thức Object.requireNonNull (T obj) - được chỉ định rõ ràng để kiểm tra tham chiếu đối tượng đó là không null, được chỉ định rõ ràng để thực hiện xác thực tham số trong các phương thức / hàm tạo và được chỉ định rõ ràng để ném NPE nếu đối tượng là null. Kết thúc
flamming_python

44

Bỏ phiếu cho lập luận của Jason Cohen vì nó được trình bày tốt. Hãy để tôi tháo dỡ nó từng bước. ;-)

  • Các javadoc NPE nói một cách rõ ràng, "sử dụng bất hợp pháp khác của các đối tượng null" . Nếu nó chỉ giới hạn trong các tình huống trong đó bộ thực thi gặp null khi không nên, tất cả các trường hợp như vậy có thể được định nghĩa ngắn gọn hơn nhiều.

  • Không thể giúp nó nếu bạn giả sử điều sai, nhưng giả sử đóng gói được áp dụng đúng cách, bạn thực sự không nên quan tâm hay chú ý xem một null có được quy định không phù hợp hay không so với việc một phương thức có phát hiện ra null không phù hợp hay không và loại bỏ ngoại lệ.

  • Tôi chọn NPE thay vì IAE vì nhiều lý do

    • Nó là cụ thể hơn về bản chất của hoạt động bất hợp pháp
    • Logic nhầm lẫn cho phép null có xu hướng rất khác với logic cho phép nhầm lẫn các giá trị bất hợp pháp. Ví dụ: nếu tôi xác thực dữ liệu được nhập bởi người dùng, nếu tôi nhận được giá trị không thể chấp nhận, nguồn gốc của lỗi đó là với người dùng cuối của ứng dụng. Nếu tôi nhận được null, đó là lỗi lập trình viên.
    • Các giá trị không hợp lệ có thể gây ra những thứ như tràn ngăn xếp, lỗi bộ nhớ, ngoại lệ phân tích cú pháp, v.v. Vì lý do này, tôi thấy IAE thực sự là CHUNG NHẤT trong tất cả các trường hợp ngoại lệ trong RuntimeException.
  • Trên thực tế, các đối số không hợp lệ khác có thể dẫn đến tất cả các loại ngoại lệ khác. UnknownhostException , FileNotFoundException , một loạt các trường hợp ngoại lệ lỗi cú pháp, IndexOutOfBoundException , lỗi xác thực, v.v., v.v.

Nói chung, tôi cảm thấy NPE bị sai lệch nhiều vì theo truyền thống đã được liên kết với mã không tuân theo nguyên tắc thất bại nhanh . Điều đó, cộng với việc JDK không thể tạo ra NPE bằng một chuỗi thông điệp thực sự đã tạo ra một tâm lý tiêu cực mạnh mẽ mà không có cơ sở. Thật vậy, sự khác biệt giữa NPE và IAE từ góc độ thời gian chạy hoàn toàn là tên. Từ quan điểm đó, bạn càng chính xác với tên, bạn càng rõ ràng hơn cho người gọi.


Sự khác biệt giữa hầu hết các ngoại lệ không được kiểm tra chỉ là tên.
Thorbjørn Ravn Andersen

20

Đó là một câu hỏi kiểu "Chiến tranh Thánh". Nói cách khác, cả hai lựa chọn thay thế đều tốt, nhưng mọi người sẽ có sở thích của họ mà họ sẽ bảo vệ cho đến chết.


Không, chỉ có một câu trả lời đúng cho câu hỏi này và đó là sử dụng ngoại lệ ném IllegalArgument khi đầu vào của phương thức không chính xác. Ngoài ra trong môi trường dev, bạn có thể sử dụng các xác nhận để kiểm tra tính hợp lệ của đầu vào và đưa ra ngoại lệ phù hợp;)
Mr.Q

@ Mr.Q Và tôi nghĩ rằng NullPointerExceptionnên bỏ đi: đó là quy ước mà JDK sử dụng và yêu cầu giao diện, nó cụ thể hơn (giống như IndexOutOfBoundsException, v.v.), v.v.
Solomon Ucko 22/07/19

kekekekkek ...: D
Yousha Aleayoub

17

Nếu đó là một setterphương pháp và nullđược truyền cho nó, tôi nghĩ sẽ có ý nghĩa hơn khi ném một cái IllegalArgumentException. Một NullPointerExceptiondường như có ý nghĩa hơn trong trường hợp bạn đang cố gắng để thực sự sử dụng null.

Vì vậy, nếu bạn đang sử dụng nó và nó null, NullPointer. Nếu nó được thông qua và nó null, IllegalArgument.


9

Apache Commons Lang có một NullArgumentException thực hiện một số điều được thảo luận ở đây: nó mở rộng IllegalArgumentException và hàm tạo duy nhất của nó lấy tên của đối số nên không phải là null.

Trong khi tôi cảm thấy rằng việc ném một cái gì đó như NullArgumentException hoặc IllegalArgumentException mô tả chính xác hơn các trường hợp đặc biệt, các đồng nghiệp của tôi và tôi đã chọn trì hoãn lời khuyên của Bloch về chủ đề này.


7
Lưu ý rằng họ đã xóa nó khỏi commons-lang3: apache-commons.680414.n4.nabble.com/ từ
artbristol

7

Không thể đồng ý nhiều hơn với những gì được nói. Thất bại sớm, thất bại nhanh. Thần chú ngoại lệ khá tốt.

Câu hỏi về việc ngoại lệ để ném chủ yếu là vấn đề sở thích cá nhân. Trong suy nghĩ của tôi, IllegalArgumentException có vẻ cụ thể hơn so với việc sử dụng NPE vì nó cho tôi biết rằng vấn đề xảy ra với một đối số mà tôi đã truyền cho phương thức chứ không phải với một giá trị có thể được tạo ra khi thực hiện phương thức.

2 lon của tôi


7

Trên thực tế, câu hỏi về việc ném IllegalArgumentException hoặc NullPulumException theo quan điểm khiêm tốn của tôi chỉ là một "cuộc chiến thần thánh" đối với một thiểu số với sự hiểu biết chưa đầy đủ về xử lý ngoại lệ trong Java. Nói chung, các quy tắc rất đơn giản và như sau:

  • vi phạm ràng buộc đối số phải được chỉ định càng nhanh càng tốt (-> thất bại nhanh), để tránh các trạng thái bất hợp pháp khó gỡ lỗi hơn nhiều
  • trong trường hợp con trỏ null không hợp lệ vì bất kỳ lý do gì, hãy ném NullPulumException
  • trong trường hợp chỉ mục mảng / bộ sưu tập bất hợp pháp, hãy ném ArrayIndexOutOfBound
  • trong trường hợp kích thước mảng / bộ sưu tập âm, hãy ném NegativeArraySizeException
  • trong trường hợp có đối số bất hợp pháp không được nêu ở trên và đối với trường hợp bạn không có loại ngoại lệ cụ thể khác, hãy ném IllegalArgumentException làm thùng rác
  • mặt khác, trong trường hợp vi phạm ràng buộc TRONG MỘT L FINH VỰC không thể tránh được bằng cách nhanh chóng thất bại vì một số lý do hợp lệ, hãy bắt và suy nghĩ lại như IllegalStateException hoặc một ngoại lệ được kiểm tra cụ thể hơn. Không bao giờ để vượt qua NullPulumException ban đầu, ArrayIndexOutOfBound, vv trong trường hợp này!

Có ít nhất ba lý do rất chính đáng để chống lại trường hợp ánh xạ tất cả các loại vi phạm ràng buộc đối số đối với IllegalArgumentException, với lý do thứ ba có thể nghiêm trọng đến mức đánh dấu phong cách xấu:

(1) Một lập trình viên không thể giả định một cách an toàn rằng tất cả các trường hợp vi phạm ràng buộc đối số đều dẫn đến IllegalArgumentException, vì phần lớn các lớp tiêu chuẩn sử dụng ngoại lệ này thay vì thùng rác nếu không có loại ngoại lệ cụ thể nào hơn. Cố gắng ánh xạ tất cả các trường hợp vi phạm ràng buộc đối số với IllegalArgumentException trong API của bạn chỉ dẫn đến sự thất vọng của lập trình viên khi sử dụng các lớp của bạn, vì các thư viện tiêu chuẩn hầu hết tuân theo các quy tắc khác nhau vi phạm và hầu hết người dùng API của bạn cũng sẽ sử dụng chúng!

(2) Ánh xạ các ngoại lệ thực sự dẫn đến một loại dị thường khác, gây ra bởi sự kế thừa duy nhất: Tất cả các ngoại lệ Java là các lớp và do đó chỉ hỗ trợ kế thừa duy nhất. Do đó, không có cách nào để tạo một ngoại lệ thực sự nói cả NullPulumException và IllegalArgumentException, vì các lớp con chỉ có thể kế thừa từ cái này hay cái khác. Do đó, việc ném IllegalArgumentException trong trường hợp không có đối số null khiến người dùng API khó phân biệt giữa các vấn đề bất cứ khi nào chương trình cố gắng sửa lỗi lập trình, ví dụ bằng cách đưa các giá trị mặc định vào lặp lại cuộc gọi!

(3) Ánh xạ thực sự tạo ra nguy cơ che giấu lỗi: Để ánh xạ các vi phạm ràng buộc đối số vào IllegalArgumentException, bạn sẽ cần mã hóa một lần thử bên ngoài trong mọi phương thức có bất kỳ đối số bị ràng buộc nào. Tuy nhiên, chỉ đơn giản là bắt RuntimeException trong khối bắt này là không cần thiết, bởi vì rủi ro ánh xạ được ghi lại bằng RuntimeExceptions được ném bởi các phương thức tự do được sử dụng trong của bạn vào IllegalArgumentException, ngay cả khi chúng không gây ra bởi vi phạm ràng buộc đối số. Vì vậy, bạn cần phải rất cụ thể, nhưng ngay cả nỗ lực đó cũng không bảo vệ bạn khỏi trường hợp bạn vô tình ánh xạ ngoại lệ thời gian chạy không có giấy tờ của một API khác (tức là lỗi) vào IllegalArgumentException API của bạn.

Mặt khác, với thực tiễn tiêu chuẩn, các quy tắc vẫn đơn giản và các nguyên nhân ngoại lệ không được làm sáng tỏ và cụ thể. Đối với người gọi phương thức, các quy tắc cũng dễ dàng: - nếu bạn gặp phải ngoại lệ thời gian chạy tài liệu dưới bất kỳ hình thức nào vì bạn đã chuyển một giá trị bất hợp pháp, hãy lặp lại cuộc gọi với một mặc định (đối với trường hợp ngoại lệ cụ thể này là cần thiết) hoặc sửa mã của bạn - nếu mặt khác bạn gặp phải một ngoại lệ thời gian chạy không được ghi lại cho một bộ đối số nhất định, hãy gửi báo cáo lỗi cho các nhà sản xuất phương thức để đảm bảo rằng mã hoặc tài liệu của họ được sửa.


6

Thực tiễn được chấp nhận nếu sử dụng IllegalArgumentException (thông báo chuỗi) để khai báo một tham số là không hợp lệ và cung cấp càng nhiều chi tiết càng tốt ... Vì vậy, để nói rằng một tham số được tìm thấy là null trong khi ngoại lệ không null, bạn sẽ làm gì đó như thế này:

if( variable == null )
    throw new IllegalArgumentException("The object 'variable' cannot be null");

Bạn hầu như không có lý do gì để ngầm sử dụng "NullPulumException". NullPulumException là một ngoại lệ được ném bởi Máy ảo Java khi bạn cố gắng thực thi mã trên tham chiếu null (Like toString () ).


6

Việc ném một ngoại lệ dành riêng cho các nullđối số (cho dù NullPointerExceptionhoặc một loại tùy chỉnh) làm cho nullthử nghiệm tự động đáng tin cậy hơn. Kiểm tra tự động này có thể được thực hiện với sự phản ánh và một tập hợp các giá trị mặc định, như trong ổi 's NullPointerTester. Ví dụ: NullPointerTestersẽ cố gắng gọi phương thức sau ...

Foo(String string, List<?> list) {
  checkArgument(string.length() > 0);
  // missing null check for list!
  this.string = string;
  this.list = list;
}

... Với hai danh sách các đối số: "", nullnull, ImmutableList.of(). Nó sẽ kiểm tra rằng mỗi cuộc gọi này sẽ ném dự kiến NullPointerException. Đối với việc thực hiện này, thông qua một nulldanh sách không tạo ra NullPointerException. Nó hiện, tuy nhiên, xảy ra để tạo ra một IllegalArgumentExceptionNullPointerTester tình cờ sử dụng một chuỗi mặc định "". Nếu NullPointerTesterchỉ mong đợi NullPointerExceptioncác nullgiá trị, nó sẽ bắt lỗi. Nếu nó mong đợi IllegalArgumentException, nó bỏ lỡ nó.


5

Một số bộ sưu tập cho rằng nullbị từ chối sử dụng NullPointerExceptionchứ không phải IllegalArgumentException. Ví dụ: nếu bạn so sánh một tập hợp chứa nullvới một tập hợp từ chối null, tập đầu tiên sẽ gọi containsAllbộ kia và bắt nó NullPointerException- nhưng không IllegalArgumentException. (Tôi đang xem việc thực hiệnAbstractSet.equals .)

Bạn một cách hợp lý có thể tranh luận rằng việc sử dụng ngoại lệ được kiểm soát theo cách này là một antipattern, mà so sánh bộ sưu tập có chứa nullcác bộ sưu tập mà không thể chứa nulllà một lỗi có khả năng thực sự nên tạo ra một ngoại lệ, hoặc rằng việc đưa nullvào một bộ sưu tập ở tất cả là một ý tưởng tồi . Tuy nhiên, trừ khi bạn sẵn sàng nói rằng equalsnên đưa ra một ngoại lệ trong trường hợp như vậy, bạn vẫn bị mắc kẹt khi nhớ rằng điều đó NullPointerExceptionlà bắt buộc trong một số trường hợp nhất định nhưng không phải trong trường hợp khác. ("IAE trước NPE ngoại trừ sau 'c' ...")


Tôi không thấy cách chứa NPE tùy thuộc vào một yếu tố bên trong bộ sưu tập. Lý do duy nhất khiến một NPE bị ném (afaict) là nếu bản thân bộ sưu tập là null (trong trường hợp đó NPE bị ném vì nó cố gắng truy cập vào iterator). Tuy nhiên, điều này đặt ra câu hỏi nếu đầu vào null nên được kiểm tra hoặc nếu nó sẽ lan truyền cho đến khi nó cố gắng truy cập.
Alowaniak

2
new TreeSet<>().containsAll(Arrays.asList((Object) null));ném NPEListchứa null.
Chris Povirk

1
Thật vậy, TreeSet # chứa ném NPE "nếu phần tử được chỉ định là null và bộ này sử dụng thứ tự tự nhiên hoặc bộ so sánh của nó không cho phép các phần tử null". Chỉ nhìn vào Tóm tắt mà không cho phép null, xấu của tôi. Cá nhân tôi thấy thật kỳ lạ khi nó không trả về false, vì trong trường hợp đó không thể có thêm null.
Alowaniak

5

Như một câu hỏi chủ quan, điều này nên được đóng lại, nhưng vì nó vẫn mở:

Đây là một phần của chính sách nội bộ được sử dụng tại nơi làm việc trước đây của tôi và nó hoạt động rất tốt. Đây là tất cả từ bộ nhớ nên tôi không thể nhớ chính xác từ ngữ. Điều đáng chú ý là họ đã không sử dụng các ngoại lệ được kiểm tra, nhưng điều đó nằm ngoài phạm vi của câu hỏi. Các ngoại lệ không được kiểm soát mà họ đã sử dụng rơi vào 3 loại chính.

NullPulumException: Đừng cố tình ném. Các NPE chỉ được ném bởi VM khi hủy bỏ tham chiếu null. Tất cả những nỗ lực có thể được thực hiện để đảm bảo rằng những thứ này không bao giờ bị ném. @Nullable và @NotNull nên được sử dụng cùng với các công cụ phân tích mã để tìm các lỗi này.

IllegalArgumentException: Bị ném khi một đối số cho hàm không phù hợp với tài liệu công khai, sao cho lỗi có thể được xác định và mô tả theo các đối số được đưa vào. Tình huống của OP sẽ thuộc loại này.

IllegalStateException: Ném khi một hàm được gọi và các đối số của nó là bất ngờ tại thời điểm chúng được truyền hoặc không tương thích với trạng thái của đối tượng mà phương thức là thành viên của.

Ví dụ: có hai phiên bản nội bộ của IndexOutOfBoundException được sử dụng trong những thứ có độ dài. Một lớp con của IllegalStateException, được sử dụng nếu chỉ mục lớn hơn chiều dài. Một lớp con khác của IllegalArgumentException, được sử dụng nếu chỉ mục là âm. Điều này là do bạn có thể thêm nhiều mục vào đối tượng và đối số sẽ hợp lệ, trong khi số âm không bao giờ hợp lệ.

Như tôi đã nói, hệ thống này hoạt động rất tốt và phải có người giải thích lý do tại sao có sự khác biệt: "Tùy thuộc vào loại lỗi, nó khá đơn giản để bạn tìm ra phải làm gì. Ngay cả khi bạn thực sự không thể hình dung được tìm ra lỗi sai, bạn có thể tìm ra nơi bắt lỗi đó và tạo thêm thông tin sửa lỗi. "

NullPulumException: Xử lý trường hợp Null hoặc đặt một xác nhận để NPE không bị ném. Nếu bạn đặt trong một khẳng định chỉ là một trong hai loại khác. Nếu có thể, tiếp tục gỡ lỗi như thể khẳng định đã ở đó ngay từ đầu.

IllegalArgumentException: bạn có điều gì đó sai ở trang web cuộc gọi của bạn. Nếu các giá trị được truyền vào là từ một hàm khác, hãy tìm hiểu lý do tại sao bạn nhận được một giá trị không chính xác. Nếu bạn truyền vào một trong các đối số của mình, lỗi sẽ kiểm tra ngăn xếp cuộc gọi cho đến khi bạn tìm thấy hàm không trả về những gì bạn mong đợi.

IllegalStateException: Bạn chưa gọi các hàm của mình theo đúng thứ tự. Nếu bạn đang sử dụng một trong các đối số của mình, hãy kiểm tra chúng và ném IllegalArgumentException mô tả vấn đề. Sau đó, bạn có thể tuyên truyền má lên trên ngăn xếp cho đến khi bạn tìm thấy vấn đề.

Dù sao, quan điểm của ông là bạn chỉ có thể sao chép IllegalArgumentAssertions lên ngăn xếp. Không có cách nào để bạn tuyên truyền IllegalStateExceptions hoặc NullPulumExceptions lên ngăn xếp vì chúng có liên quan đến chức năng của bạn.


4

Nói chung, nhà phát triển không bao giờ nên ném NullPulumException. Ngoại lệ này được ném bởi bộ thực thi khi mã cố gắng hủy đăng ký một biến có giá trị là null. Do đó, nếu phương thức của bạn muốn không cho phép null một cách rõ ràng, trái ngược với việc chỉ xảy ra để có giá trị null nâng cao NullPulumException, bạn nên ném IllegalArgumentException.


9
JavaDoc trên NPE có ý kiến ​​khác: "Các ứng dụng nên ném các thể hiện của lớp này để chỉ ra việc sử dụng bất hợp pháp khác của đối tượng null." Đừng quá phân loại
Donz

4

Tôi muốn loại bỏ các đối số Null khỏi các đối số bất hợp pháp khác, vì vậy tôi đã nhận được một ngoại lệ từ IAE có tên là NullArgumentException. Thậm chí không cần đọc thông báo ngoại lệ, tôi biết rằng một đối số null được truyền vào một phương thức và bằng cách đọc tin nhắn, tôi tìm ra đối số nào là null. Tôi vẫn bắt được NullArgumentException với trình xử lý IAE, nhưng trong nhật ký của tôi là nơi tôi có thể thấy sự khác biệt nhanh chóng.


Tôi đã áp dụng phương pháp "ném IllegalArgumentException (" foo == null ")" mới. Dù sao, bạn cũng cần phải đăng nhập tên biến (để chắc chắn rằng bạn đang nhìn đúng tiêu chuẩn, v.v.)
Thorbjørn Ravn Andersen

4

sự phân đôi ... Chúng không chồng chéo? Chỉ các phần không chồng chéo của một tổng thể có thể tạo ra một sự phân đôi. Theo tôi thấy:

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

1
Điều này sẽ tăng gấp đôi chi phí cho việc tạo ngoại lệ và sẽ không thực sự hữu ích vì việc bắt NullPointerExceptionsẽ không làm gì cả. Điều duy nhất có thể giúp là IllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException, nhưng chúng tôi không có nhiều quyền thừa kế.
maaartinus

Tôi tin rằng các ngoại lệ cụ thể hơn nên được bao bọc bởi các ngoại lệ chung hơn. NPE là cho một biểu thức trong khi IAE là cho một phương thức. Vì các phương thức chứa các câu lệnh chứa các biểu thức, IAE tổng quát hơn.
SGene9

Về chi phí, tôi không có ý kiến ​​gì. Nhưng vì stacktraces về cơ bản sẽ giống hệt nhau ngoại trừ tên của ngoại lệ đã thay đổi ở giữa, tôi nghĩ rằng không nên có quá nhiều chi phí cho các ngoại lệ kép. Nếu một người lo lắng về chi phí chung, anh ta có thể sử dụng các câu lệnh "nếu" để trả về null hoặc -1 thay vì ném ngoại lệ.
SGene9

4

NullPointerExceptionbị ném khi cố gắng truy cập một đối tượng có biến tham chiếu có giá trị hiện tại là null.

IllegalArgumentException được ném khi một phương thức nhận được một đối số được định dạng khác với phương thức mong đợi.


3

Theo kịch bản của bạn, IllegalArgumentExceptionlà lựa chọn tốt nhất, vì nullkhông phải là giá trị hợp lệ cho tài sản của bạn.


0

Lý tưởng thời gian chạy không nên được ném. Một ngoại lệ được kiểm tra (ngoại lệ kinh doanh) nên được tạo cho kịch bản của bạn. Bởi vì nếu một trong hai ngoại lệ này bị ném và ghi nhật ký, nó sẽ hiểu sai về nhà phát triển trong khi duyệt qua nhật ký. Thay vào đó, các ngoại lệ kinh doanh không tạo ra sự hoảng loạn đó và thường bị bỏ qua trong khi xử lý sự cố nhật ký.


-1

Các định nghĩa từ các liên kết đến hai trường hợp ngoại lệ ở trên là IllegalArgumentException: Thrown để chỉ ra rằng một phương thức đã được thông qua một đối số bất hợp pháp hoặc không phù hợp. NullPulumException: Ném khi ứng dụng cố gắng sử dụng null trong trường hợp bắt buộc phải có đối tượng.

Sự khác biệt lớn ở đây là IllegalArgumentException được cho là sẽ được sử dụng khi kiểm tra xem một đối số cho một phương thức có hợp lệ không. NullPulumException được cho là sẽ được sử dụng bất cứ khi nào một đối tượng được "sử dụng" khi nó là null.

Tôi hy vọng điều đó sẽ giúp đưa hai người vào quan điểm.


1
Điểm nổi bật là ứng dụng đang sử dụng null chứ không phải thời gian chạy. Vì vậy, có một sự chồng chéo khá lớn giữa "khi một phương thức đã được thông qua một đối số bất hợp pháp hoặc không phù hợp" và "khi một ứng dụng đang sử dụng null". Về lý thuyết, nếu một ứng dụng vượt qua null cho một trường yêu cầu không null, cả hai tiêu chí đều được đáp ứng.
Christopher Smith

-1

Nếu đó là "setter" hoặc ở đâu đó tôi sẽ trở thành thành viên để sử dụng sau này, tôi có xu hướng sử dụng IllegalArgumentException.

Nếu đó là thứ gì đó tôi sẽ sử dụng (dereference) ngay bây giờ trong phương thức, tôi sẽ chủ động ném NullPulumException. Tôi thích điều này tốt hơn là để cho bộ thực thi thực hiện nó, bởi vì tôi có thể cung cấp một thông điệp hữu ích (có vẻ như bộ thực thi cũng có thể làm điều này, nhưng đó là một lời ca ngợi cho một ngày khác).

Nếu tôi ghi đè một phương thức, tôi sử dụng bất cứ phương thức nào được ghi đè sử dụng.


-1

Bạn nên ném IllegalArgumentException, vì nó sẽ làm cho lập trình viên thấy rõ rằng anh ta đã làm điều gì đó không hợp lệ. Các nhà phát triển đã quá quen với việc thấy NPE bị VM ném, đến nỗi bất kỳ lập trình viên nào cũng sẽ không nhận ra ngay lỗi của mình và sẽ bắt đầu tìm kiếm ngẫu nhiên, hoặc tệ hơn, đổ lỗi cho mã của bạn là 'lỗi'.


4
Xin lỗi, nếu một lập trình viên nhìn xung quanh "ngẫu nhiên" khi nhận được bất kỳ loại ngoại lệ nào ... việc thay đổi tên của một ngoại lệ sẽ không giúp ích nhiều.
Christopher Smith

-1

Trong trường hợp này, IllegalArgumentException truyền đạt thông tin rõ ràng cho người dùng bằng API của bạn rằng "không nên rỗng". Như những người dùng diễn đàn khác đã chỉ ra rằng bạn có thể sử dụng NPE nếu bạn muốn miễn là bạn truyền đạt đúng thông tin cho người dùng bằng API của mình.

GaryF và Tutt đã bỏ các tài liệu tham khảo "Java hiệu quả" (mà tôi thề là) khuyến nghị sử dụng NPE. Và xem cách các API tốt khác được xây dựng là cách tốt nhất để xem cách xây dựng API của bạn.

Một ví dụ điển hình khác là xem xét các API mùa xuân. Ví dụ: org.springframework.beans.BeanUtils.instantiateClass (Con constructor ctor, Object [] args) có dòng Assert.notNull (ctor, "Con constructor không được null"). Phương thức org.springframework.util.Assert.notNull (Object object, String message) kiểm tra xem liệu đối số (object) được truyền vào có rỗng không và nếu nó ném ra một IllegalArgumentException (message) mới mà sau đó được bắt trong org. phương thức springframework.beans.BeanUtils.instantiateClass (...).


-5

Nếu bạn chọn ném NPE và bạn đang sử dụng đối số trong phương thức của mình, thì có thể là dư thừa và tốn kém để kiểm tra rõ ràng cho null. Tôi nghĩ rằng VM đã làm điều đó cho bạn.


Thời gian chạy sẽ không bao gồm một thông điệp có ý nghĩa.
mP.

Trên thực tế nhận xét này có thể rút ra một số ý kiến ​​khác. Hãy để VM nói chuyện với NPEcác lập trình viên nói IAEtrước VM nếu họ muốn.
Jin Kwon

1
Đắt? Tôi không nghĩ rằng == null đắt như vậy ... Bên cạnh đó, đối số null có thể chỉ được lưu trữ để sử dụng sau và sẽ đưa ra một ngoại lệ lâu sau khi gọi phương thức, khiến lỗi khó theo dõi hơn. Hoặc bạn có thể tạo một đối tượng đắt tiền trước khi sử dụng đối số null, v.v. Phát hiện sớm dường như là một lựa chọn tốt.
PhiLho

bỏ phiếu của câu trả lời này là sự hiểu lầm về phần cứng phía sau. Chắc chắn các kiểm tra phần cứng (CPU đó) rẻ hơn mà kiểm tra rõ ràng. Dereferences of null là trường hợp đặc biệt Seg SegFault (SegV) (truy cập vào một trang không thuộc sở hữu của quy trình) mà CPU / OS kiểm tra và JRE xử lý như một trường hợp đặc biệt ném NullPulumException.
digital_infinity
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.