Đôi khi Boolean.valueOf () tạo ra NullPulumException


115

Tôi có mã này:

package tests;

import java.util.Hashtable;

public class Tests {

    public static void main(String[] args) {

        Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();

        System.out.println("TEST 1");
        System.out.println(modifiedItems.get("item1")); // Prints null
        System.out.println("TEST 2");
        System.out.println(modifiedItems.get("item1") == null); // Prints true
        System.out.println("TEST 3");
        System.out.println(Boolean.valueOf(null)); // Prints false
        System.out.println("TEST 4");
        System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
        System.out.println("FINISHED!"); // Never executed
    }
}

Vấn đề của tôi là tôi không hiểu tại sao Test 3 hoạt động tốt (nó in falsevà không sản xuất NullPointerException) trong khi Test 4 ném a NullPointerException. Như bạn có thể thấy trong các thử nghiệm 12 , nullmodifiedItems.get("item1")bằng và null.

Hành vi này giống nhau trong Java 7 và 8.


sửa đổiItems.get ("item1") này là null, bạn có biết điều đó không, nhưng bạn cho rằng việc chuyển cái này đến valueOf sẽ không kết thúc trong NPE?
Stultuske

16
@Stultuske: Đó là một câu hỏi hợp lệ, chỉ cần hai dòng trên chuyển một chữ theo nullcùng một hàm sẽ không tạo ra NPE! Có một lý do chính đáng cho nó, nhưng nó chắc chắn gây nhầm lẫn ngay từ cái nhìn đầu tiên :-)
psmears

25
Tôi rất ấn tượng. Đây là câu hỏi ngoại lệ con trỏ null thú vị nhất tôi từng thấy trong nhiều năm.
candied_orange

@Jeroen đây không phải là một bản sao của câu hỏi đó . Mặc dù sự thật là unboxing là phổ biến cho hai vấn đề, không có so sánh đang diễn ra ở đây. Điều quan trọng về câu hỏi này là nó xảy ra do cách giải quyết quá tải; và đó là một điều hoàn toàn khác với cách ==áp dụng.
Andy Turner

Câu trả lời:


178

Bạn phải xem xét kỹ xem quá tải đang được gọi:

  • Boolean.valueOf(null)đang cầu khẩn Boolean.valueOf(String). Điều này không ném NPEngay cả khi được cung cấp với một tham số null.
  • Boolean.valueOf(modifiedItems.get("item1"))đang gọi Boolean.valueOf(boolean), bởi vì modifiedItemscác giá trị thuộc loại Boolean, yêu cầu chuyển đổi unboxing. Kể từ khi modifiedItems.get("item1")null, nó là unboxing của giá trị đó - không phải là Boolean.valueOf(...)- mà ném NPE.

Các quy tắc để xác định quá tải nào được gọi là khá nhiều lông , nhưng chúng đại khái như thế này:

  • Trong lần đầu tiên, một kết hợp phương thức được tìm kiếm mà không cho phép đấm bốc / bỏ hộp (cũng không phải là phương pháp arity biến).

    • Bởi vì nulllà một giá trị chấp nhận được cho một Stringnhưng không boolean, Boolean.valueOf(null)được khớp với Boolean.valueOf(String)trong vượt qua này;
    • Booleankhông thể chấp nhận được cho một trong hai Boolean.valueOf(String)hoặc Boolean.valueOf(boolean), vì vậy không có phương thức nào phù hợp trong pass này cho Boolean.valueOf(modifiedItems.get("item1")).
  • Trong một lượt đi thứ hai, một kết hợp phương thức được tìm kiếm, cho phép đấm bốc / bỏ hộp (nhưng vẫn không thay đổi phương thức arity).

    • A Booleancó thể được bỏ hộp đến boolean, vì vậy Boolean.valueOf(boolean)được khớp với Boolean.valueOf(modifiedItems.get("item1"))trong pass này; nhưng một trình chuyển đổi unboxing phải được trình biên dịch chèn vào để gọi nó:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (Có một đường chuyền thứ ba cho phép các phương thức arity khác nhau, nhưng điều đó không liên quan ở đây, vì hai đường chuyền đầu tiên khớp với các trường hợp này)


3
Mã có thể rõ ràng hơn nếu chúng ta sử dụng Boolean.valueOf(modifiedItems.get("item1").booleanValue())trong mã nguồn thay vì Boolean.valueOf(modifiedItems.get("item1"))?
CausingUnderflowsEverywhere 6/10/2017

1
@CausingUnderflowsEverywhere không thực sự - thật khó để thấy điều đó .booleanValue()bị chôn vùi trong biểu thức. Hai quan sát: 1) quyền anh tự động (un) là một tính năng có chủ ý của Java để loại bỏ cú pháp cú pháp; tự làm nó là có thể, nhưng không thành ngữ; 2) điều này hoàn toàn không giúp ích gì cho bạn - chắc chắn nó không ngăn chặn sự cố xảy ra, cũng như không cung cấp thêm thông tin nào khi xảy ra lỗi (dấu vết ngăn xếp sẽ giống hệt nhau, vì mã được thực thi giống hệt nhau).
Andy Turner

@CausingUnderflows Ở mọi nơi, tốt hơn là sử dụng công cụ để làm nổi bật các vấn đề, ví dụ intellij sẽ kiếm cho bạn về NPE tiềm năng ở đây.
Andy Turner

13

modifiedItems.gettrả về một Boolean( không thể chuyển thành a String), chữ ký sẽ được sử dụng là Boolean.valueOf(boolean), trong đó hộp thư Booleanđược đặt ở dạng nguyên thủy boolean. Khi nullđược trả lại ở đó, hộp thư đi không thành công với a NullPointerException.


11

Chữ ký phương thức

Phương pháp Boolean.valueOf(...)có hai chữ ký:

  1. public static Boolean valueOf(boolean b)
  2. public static Boolean valueOf(String s)

modifiedItemsGiá trị của bạn là Boolean. Bạn không thể cast Booleancho Stringnên hậu quả là chữ ký đầu tiên sẽ được chọn

Boolean unboxing

Trong tuyên bố của bạn

Boolean.valueOf(modifiedItems.get("item1"))

có thể được đọc là

Boolean.valueOf(modifiedItems.get("item1").booleanValue())   

Tuy nhiên, modifiedItems.get("item1")trả lại nullđể về cơ bản bạn sẽ có

null.booleanValue()

mà rõ ràng dẫn đến một NullPointerException


Từ ngữ không chính xác, cảm ơn bạn đã chỉ ra và trả lời được cập nhật theo phản hồi của bạn. Xin lỗi, tôi đã không thấy câu trả lời của bạn trong khi viết và tôi thấy rằng tôi giống như của bạn. Tôi có nên xóa câu trả lời của mình để tránh nhầm lẫn cho OP không?
Al-un

4
Đừng xóa nó trên tài khoản của tôi. Hãy nhớ rằng, đây không phải là một trò chơi có tổng bằng không: mọi người có thể (và làm) đưa ra nhiều câu trả lời.
Andy Turner

3

Như Andy đã mô tả rất rõ lý do NullPointerException:

đó là do Boolean un-boxing:

Boolean.valueOf(modifiedItems.get("item1"))

được chuyển đổi thành:

Boolean.valueOf(modifiedItems.get("item1").booleanValue())

tại thời gian chạy và sau đó nó ném NullPointerExceptionnếu modifiedItems.get("item1")là null.

Bây giờ tôi muốn thêm một điểm nữa ở đây rằng việc bỏ quyền anh của các lớp sau vào các nguyên hàm tương ứng của chúng cũng có thể tạo ra NullPointerExceptionngoại lệ nếu các đối tượng trả về tương ứng của chúng là null.

  1. byte - Byte
  2. char - Nhân vật
  3. phao - phao
  4. int - Số nguyên
  5. dài dài
  6. ngắn ngắn
  7. gấp đôi

Đây là mã:

    Hashtable<String, Boolean> modifiedItems1 = new Hashtable<String, Boolean>();
    System.out.println(Boolean.valueOf(modifiedItems1.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Byte> modifiedItems2 = new Hashtable<String, Byte>();
    System.out.println(Byte.valueOf(modifiedItems2.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Character> modifiedItems3 = new Hashtable<String, Character>();
    System.out.println(Character.valueOf(modifiedItems3.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Float> modifiedItems4 = new Hashtable<String, Float>();
    System.out.println(Float.valueOf(modifiedItems4.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Integer> modifiedItems5 = new Hashtable<String, Integer>();
    System.out.println(Integer.valueOf(modifiedItems5.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Long> modifiedItems6 = new Hashtable<String, Long>();
    System.out.println(Long.valueOf(modifiedItems6.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Short> modifiedItems7 = new Hashtable<String, Short>();
    System.out.println(Short.valueOf(modifiedItems7.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Double> modifiedItems8 = new Hashtable<String, Double>();
    System.out.println(Double.valueOf(modifiedItems8.get("item1")));//Exception in thread "main" java.lang.NullPointerException

1
"Được chuyển đổi thành ... trong thời gian chạy", nó được chuyển đổi thành thời gian biên dịch.
Andy Turner

0

Một cách để hiểu nó là khi Boolean.valueOf(null)được gọi, java chính xác được nói để đánh giá null.

Tuy nhiên, khi Boolean.valueOf(modifiedItems.get("item1"))được gọi, java được yêu cầu lấy một giá trị từ HashTable của loại đối tượng Boolean, nhưng nó không tìm thấy loại Boolean mà nó tìm thấy một ngõ cụt thay thế (null) mặc dù nó mong đợi Boolean. Ngoại lệ NullPulumException được đưa ra bởi vì những người tạo ra phần này của java đã quyết định tình huống này là một trường hợp của một chương trình bị lỗi cần sự chú ý của lập trình viên. (Một cái gì đó ngoài ý muốn đã xảy ra.)

Trong trường hợp này, sự khác biệt nhiều hơn giữa việc cố tình tuyên bố rằng bạn dự định null ở đó và java tìm thấy một tham chiếu bị thiếu cho một đối tượng (null) nơi tìm thấy một đối tượng.

Xem thêm thông tin về NullPulumException trong câu trả lời này: https://stackoverflow.com/a/25721181/4425643


Nếu ai đó có thể giúp cải thiện câu trả lời này, tôi đã nghĩ đến một từ đề cập đến lập trình viên viết một cái gì đó với ý định rõ ràng, không có sự mơ hồ
CausingUnderflowsEverywhere
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.