Boolean.valueOf () สร้าง NullPointerException ในบางครั้ง


115

ฉันมีรหัสนี้:

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
    }
}

ปัญหาของฉันคือฉันไม่เข้าใจว่าทำไมTest 3 ถึงทำงานได้ดี (มันพิมพ์ออกมาfalseและไม่ได้ผลิตNullPointerException) ในขณะที่Test 4พ่นไฟล์NullPointerException. ในขณะที่คุณสามารถมองเห็นในการทดสอบ1และ2 , nullและเท่าเทียมกันและmodifiedItems.get("item1")null

ลักษณะการทำงานจะเหมือนกันใน Java 7 และ 8


modifiedItems.get ("item1") นี่เป็นโมฆะคุณทราบดี แต่คุณคิดว่าการส่งต่อไปยัง valueOf จะไม่จบลงใน NPE?
Stultuske

16
@Stultuske: เป็นคำถามที่ถูกต้องเนื่องจากมีเพียงสองบรรทัดด้านบนที่ส่งผ่านตัวอักษรnullไปยังฟังก์ชันเดียวกันไม่ได้สร้าง NPE! มันมีเหตุผลที่ดี แต่ก็ทำให้สับสนตั้งแต่แรกเห็น :-)
psmears

25
ฉันประทับใจ. นี่เป็นคำถามยกเว้นตัวชี้โมฆะที่น่าสนใจที่สุดที่ฉันเคยเห็นในรอบหลายปี
candied_orange

@ เจโรนนี่ไม่ใช่การหลอกลวงของคำถามนั้น แม้ว่าจะเป็นเรื่องจริงที่การแกะกล่องเป็นเรื่องปกติของปัญหาทั้งสอง แต่ก็ไม่มีการเปรียบเทียบเกิดขึ้นที่นี่ สิ่งสำคัญเกี่ยวกับคำถามนี้คือมันเกิดขึ้นเนื่องจากวิธีแก้ปัญหาการโอเวอร์โหลด และนั่นเป็นสิ่งที่แตกต่างจากวิธี==การนำไปใช้
Andy Turner

คำตอบ:


178

คุณต้องดูอย่างรอบคอบว่ามีการเรียกใช้โอเวอร์โหลดใด:

  • Boolean.valueOf(null)Boolean.valueOf(String)คือการกล่าวอ้าง สิ่งนี้ไม่ส่งผลNPEแม้ว่าจะมีพารามิเตอร์ null ก็ตาม
  • Boolean.valueOf(modifiedItems.get("item1"))กำลังเรียกใช้Boolean.valueOf(boolean)เนื่องจากmodifiedItemsค่าเป็นประเภทBooleanซึ่งต้องมีการแปลงแบบ unboxing ตั้งแต่modifiedItems.get("item1")เป็นnullมันเป็นแกะกล่องของมูลค่าที่ - ไม่Boolean.valueOf(...)- ซึ่งพ่น NPE

กฎสำหรับการพิจารณาว่ามีการเรียกใช้โอเวอร์โหลดใดจะมีขนดกแต่โดยประมาณจะเป็นดังนี้:

  • ในรอบแรกวิธีการจับคู่จะถูกค้นหาโดยไม่อนุญาตให้ชกมวย / unboxing (หรือวิธีอาริตีแบบแปรผัน)

    • เพราะnullเป็นค่าที่ยอมรับได้สำหรับStringแต่ไม่boolean, Boolean.valueOf(null)ตรงกับBoolean.valueOf(String)ในบัตรผ่านนี้
    • Booleanไม่ได้เป็นที่ยอมรับสำหรับการอย่างใดอย่างหนึ่งBoolean.valueOf(String)หรือดังนั้นวิธีการที่ไม่ถูกจับคู่ในการส่งผ่านนี้Boolean.valueOf(boolean)Boolean.valueOf(modifiedItems.get("item1"))
  • ในรอบที่สองจะมีการค้นหาเมธอดจับคู่ซึ่งอนุญาตให้ชกมวย / แกะกล่อง (แต่ยังไม่ใช่เมธอด arity ที่แปรผัน)

    • A Booleanสามารถแกะกล่องได้booleanดังนั้นจึงBoolean.valueOf(boolean)ถูกจับคู่Boolean.valueOf(modifiedItems.get("item1"))ในบัตรนี้ แต่คอมไพเลอร์จะต้องแทรกการแปลง unboxing เพื่อเรียกใช้:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (มีบัตรผ่านที่สามที่อนุญาตให้ใช้วิธี arity ตัวแปรได้ แต่ไม่เกี่ยวข้องที่นี่เนื่องจากสองรอบแรกตรงกับกรณีเหล่านี้)


3
โค้ดจะชัดเจนกว่านี้ไหมถ้าเราใช้Boolean.valueOf(modifiedItems.get("item1").booleanValue())ในซอร์สโค้ดแทนBoolean.valueOf(modifiedItems.get("item1"))?
CauseUnderflowsEverywhere

1
@CausingUnderflowsEverywhere ไม่จริง - มันยากจริงๆที่จะเห็นสิ่งนั้น.booleanValue()ฝังอยู่ในนิพจน์ ข้อสังเกตสองประการ: 1) การชกมวยอัตโนมัติ (un) เป็นคุณลักษณะโดยเจตนาของ Java ในการลบปมไวยากรณ์ ทำด้วยตัวเองเป็นไปได้ แต่ไม่ใช่สำนวน 2) สิ่งนี้ไม่ได้ช่วยคุณเลย - แน่นอนว่ามันไม่ได้หยุดปัญหาไม่ให้เกิดขึ้นและไม่ให้ข้อมูลเพิ่มเติมใด ๆ เมื่อเกิดความล้มเหลว (การติดตามสแต็กจะเหมือนกันเนื่องจากโค้ดที่เรียกใช้งานเหมือนกัน)
Andy Turner

ทุกที่จะดีกว่าที่จะใช้เครื่องมือเพื่อเน้นปัญหาเช่น intellij จะทำให้คุณได้รับ NPE ที่มีศักยภาพที่นี่
Andy Turner

13

เนื่องจากmodifiedItems.getผลตอบแทนBoolean(ซึ่งไม่ castable ไปString) ลายเซ็นที่จะใช้เป็นBoolean.valueOf(boolean)ที่Booleanอยู่ใน outboxed booleanเพื่อดั้งเดิม เมื่อnullถูกส่งกลับไปที่นั่นการเอาท์บ็อกซ์จะล้มเหลวด้วยไฟล์NullPointerException.


11

ลายเซ็นของวิธีการ

วิธีBoolean.valueOf(...)นี้มีสองลายเซ็น:

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

ของคุณมีค่าmodifiedItems Booleanคุณไม่สามารถส่งBooleanให้ได้Stringดังนั้นลายเซ็นแรกจะถูกเลือก

การแกะกล่องบูลีน

ในคำชี้แจงของคุณ

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

ซึ่งสามารถอ่านได้ว่า

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

อย่างไรก็ตามmodifiedItems.get("item1")ผลตอบแทนnullโดยทั่วไปคุณจะได้รับ

null.booleanValue()

ซึ่งนำไปสู่ไฟล์ NullPointerException


ข้อความไม่ถูกต้องขอบคุณสำหรับการชี้และคำตอบได้รับการอัปเดตตามความคิดเห็นของคุณ ขอโทษฉันไม่เห็นคำตอบของคุณในขณะที่เขียนและฉันเห็นว่าของฉันดูเหมือนของคุณ ฉันควรลบคำตอบเพื่อหลีกเลี่ยงความสับสนสำหรับ OP หรือไม่?
Al-un

4
อย่าลบมันในบัญชีของฉัน โปรดจำไว้ว่านี่ไม่ใช่เกมที่มีผลรวมเป็นศูนย์: ผู้คนสามารถ (และทำ) เพิ่มคะแนนคำตอบได้หลายคำตอบ
Andy Turner

3

ดังที่แอนดี้อธิบายไว้เป็นอย่างดีถึงเหตุผลของNullPointerException:

ซึ่งเกิดจากการยกเลิกการชกมวยแบบบูลีน:

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

ได้รับการแปลงเป็น:

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

ที่รันไทม์จากนั้นจะโยนNullPointerExceptionถ้าmodifiedItems.get("item1")เป็นโมฆะ

ตอนนี้ฉันต้องการเพิ่มอีกหนึ่งประเด็นที่นี่ว่าการยกเลิกการชกมวยของคลาสต่อไปนี้ไปยังดั้งเดิมตามลำดับสามารถสร้างNullPointerExceptionข้อยกเว้นได้หากวัตถุที่ส่งคืนที่เกี่ยวข้องเป็นโมฆะ

  1. ไบต์ - ไบต์
  2. ถ่าน - ตัวละคร
  3. ลอย - ลอย
  4. int - จำนวนเต็ม
  5. ยาว - ยาว
  6. สั้น - สั้น
  7. คู่ - คู่

นี่คือรหัส:

    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
"แปลงเป็น ... ขณะรันไทม์" จะถูกแปลงเป็นเวลาคอมไพล์
Andy Turner

0

วิธีที่จะทำความเข้าใจคือเมื่อBoolean.valueOf(null)ถูกเรียกใช้ java จะถูกบอกให้ประเมินค่า null อย่างแม่นยำ

อย่างไรก็ตามเมื่อBoolean.valueOf(modifiedItems.get("item1"))ถูกเรียกใช้ java จะได้รับคำสั่งให้รับค่าจาก HashTable ของประเภทวัตถุบูลีน แต่ไม่พบประเภทบูลีนที่พบทางตันแทน (null) แม้ว่าจะคาดหวังบูลีนก็ตาม ข้อยกเว้น NullPointerException ถูกยกเลิกเนื่องจากผู้สร้างส่วนนี้ของ java ตัดสินใจว่าสถานการณ์นี้เป็นตัวอย่างของบางสิ่งบางอย่างในโปรแกรมที่ผิดพลาดซึ่งต้องได้รับการเอาใจใส่จากโปรแกรมเมอร์ (มีบางอย่างเกิดขึ้นโดยไม่ได้ตั้งใจ)

ในกรณีนี้ความแตกต่างระหว่างการจงใจประกาศโดยเจตนาว่าคุณต้องการให้โมฆะอยู่ที่นั่นและ java ค้นหาการอ้างอิงที่ขาดหายไปไปยังวัตถุ (null) ซึ่งมีวัตถุประสงค์ให้พบวัตถุ

ดูข้อมูลเพิ่มเติมเกี่ยวกับ NullPointerException ในคำตอบนี้: https://stackoverflow.com/a/25721181/4425643


หากมีใครสามารถช่วยปรับปรุงคำตอบนี้ได้ฉันกำลังคิดถึงคำที่หมายถึงโปรแกรมเมอร์ที่เขียนบางสิ่งด้วยเจตนาที่ชัดเจนโดยไม่มีความคลุมเครือ
CauseUnderflowsEverywhere
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.