ทำไม String.valueOf (null) จึงโยน NullPointerException


127

ตามเอกสารวิธีการString.valueOf(Object obj)คืนค่า:

ถ้าอาร์กิวเมนต์เป็นnullสตริงเท่ากับ"null"; มิฉะนั้นมูลค่าของobj.toString()จะถูกส่งกลับ

แต่เมื่อฉันพยายามทำสิ่งนี้:

System.out.println("String.valueOf(null) = " + String.valueOf(null));

มันพ่น NPE แทน? (ลองเองถ้าไม่เชื่อ!)

    ข้อยกเว้นในเธรด "main" java.lang.NullPointerException
    ที่ java.lang.String. (ไม่ทราบแหล่งที่มา)
    ที่ java.lang.String.valueOf (ไม่ทราบแหล่งที่มา)

มันเกิดขึ้นได้อย่างไร? เอกสารโกหกฉันหรือเปล่า นี่เป็นข้อผิดพลาดที่สำคัญใน Java หรือไม่?

คำตอบ:


201

ปัญหาคือString.valueOfวิธีนี้มีมากเกินไป :

Java Specification Language กำหนดว่าในกรณีเหล่านี้จะมีการเลือกโอเวอร์โหลดที่เฉพาะเจาะจงที่สุด :

JLS 15.12.2.5 การเลือกวิธีที่เฉพาะเจาะจงที่สุด

ถ้าเมธอดสมาชิกมากกว่าหนึ่งวิธีสามารถเข้าถึงได้และใช้ได้กับการเรียกใช้เมธอดจำเป็นต้องเลือกวิธีใดวิธีหนึ่งเพื่อระบุตัวอธิบายสำหรับการจัดส่งเมธอดรันไทม์ ภาษาโปรแกรม Java ใช้กฎที่เลือกวิธีที่เฉพาะเจาะจงที่สุด

char[] เป็นข้อ Objectแต่ไม่ทั้งหมดเป็น-AObject char[]ดังนั้นจึงchar[]มีความเฉพาะเจาะจงมากกว่าObjectและตามที่ภาษา Java ระบุไว้การString.valueOf(char[])โอเวอร์โหลดจะถูกเลือกในกรณีนี้

String.valueOf(char[])คาดว่าอาร์เรย์จะไม่ใช่nullและเนื่องจากnullได้รับในกรณีนี้จึงพ่นNullPointerExceptionจะได้รับในกรณีนี้มันแล้วโยน

"แก้ไข" ที่ง่ายคือการโยนnullอย่างชัดเจนObjectเป็นดังนี้:

System.out.println(String.valueOf((Object) null));
// prints "null"

คำถามที่เกี่ยวข้อง


นิทานสอนใจ

มีหลายสิ่งที่สำคัญ:

  • มีผลบังคับใช้ Java 2nd Edition, รายการ 41: ใช้มากเกินไปอย่างรอบคอบ
    • เพียงเพราะคุณสามารถทำงานหนักเกินไปไม่ได้หมายความว่าคุณควรทำทุกครั้ง
    • อาจทำให้เกิดความสับสน (โดยเฉพาะอย่างยิ่งหากวิธีการทำสิ่งที่แตกต่างกันอย่างมาก)
  • เมื่อใช้ IDE ที่ดีคุณสามารถตรวจสอบว่าโอเวอร์โหลดใดถูกเลือกในเวลาคอมไพล์
    • กับคราสคุณสามารถเมาส์เลื่อนการแสดงออกดังกล่าวและเห็นว่าจริง ๆ แล้วที่valueOf(char[])เกินจะถูกเลือก!
  • บางครั้งคุณต้องการแคสต์อย่างชัดเจนnull(ตัวอย่างที่จะติดตาม)

ดูสิ่งนี้ด้วย


ในการคัดเลือกนักแสดง null

มีสถานการณ์อย่างน้อยสองสถานการณ์ที่nullจำเป็นต้องระบุประเภทการอ้างอิงอย่างชัดเจน:

  • เพื่อเลือกการโอเวอร์โหลด (ตามตัวอย่างด้านบน)
  • เพื่อให้nullเป็นอาร์กิวเมนต์เดียวกับพารามิเตอร์ vararg

ตัวอย่างง่ายๆของหลังมีดังต่อไปนี้:

static void vararg(Object... os) {
    System.out.println(os.length);
}

จากนั้นเราสามารถมีสิ่งต่อไปนี้:

vararg(null, null, null); // prints "3"
vararg(null, null);       // prints "2"
vararg(null);             // throws NullPointerException!

vararg((Object) null);    // prints "1"

ดูสิ่งนี้ด้วย

คำถามที่เกี่ยวข้อง


5
ข้อเสนอแนะอื่น: เมื่อคุณโอเวอร์โหลดเมธอดและอาร์กิวเมนต์หนึ่งสามารถเรียกใช้กับโอเวอร์โหลดสองตัว (เช่นnullในกรณีนี้) ตรวจสอบให้แน่ใจว่าโอเวอร์โหลดทั้งสองทำหน้าที่เหมือนกันกับค่านั้น!
Joachim Sauer

3
@ Joachim: ฉันเพิ่งอ่านรายการและรู้สึกประหลาดใจอย่างมากที่พบว่าทั้งสองวิธีนี้มีการพูดคุยกันอย่างชัดเจน! Bloch ก้าวไปอีกขั้นและอ้างว่าตั้งแต่นั้นมาString.valueOf(Object)และvalueOf(char[])ทำสิ่งที่แตกต่างไปจากเดิมอย่างสิ้นเชิง ( nullไม่ว่าจะหรือไม่ก็ตาม) ว่าบางทีพวกเขาไม่ควรมีงานมากเกินไปตั้งแต่แรก
polygenelubricants

มีคำถาม ... ถ้าคุณมีเมธอดที่ส่งคืน Object แล้วคำสั่งreturn null;นั้นจะเหมือนกับreturn (Object) null;?
user972276

17

ปัญหาคือว่าคุณโทรString.valueOf(char[])และไม่ได้ String.valueOf(Object)

เหตุผลก็คือ Java มักจะเลือกเวอร์ชันที่เฉพาะเจาะจงที่สุดของเมธอดโอเวอร์โหลดที่ทำงานกับพารามิเตอร์ที่ให้มา nullเป็นค่าที่ถูกต้องสำหรับObjectพารามิเตอร์ แต่ก็เป็นค่าที่ถูกต้องสำหรับchar[]พารามิเตอร์ด้วย

ในการทำให้ Java ใช้Objectเวอร์ชันนี้ให้ส่งnullผ่านตัวแปรหรือระบุการส่งแบบชัดแจ้งไปยัง Object:

Object o = null;
System.out.println("String.valueOf(null) = " + String.valueOf(o));
// or
System.out.println("String.valueOf(null) = " + String.valueOf((Object) null));

5

มีการยื่นข้อบกพร่องหมายเลข4867608ด้วยวิธีนี้ในปี 2546 ซึ่งได้รับการแก้ไขว่า "จะไม่แก้ไข" ด้วยคำอธิบายนี้

เราไม่สามารถเปลี่ยนแปลงได้เนื่องจากข้อ จำกัด ด้านความเข้ากันได้ โปรดสังเกตว่ามันเป็นวิธี public static String valueOf (char data []) ซึ่งจะถูกเรียกใช้และไม่ได้กล่าวถึงการแทนที่ "null" สำหรับอาร์กิวเมนต์ null

@ ###. ### 2546-05-23


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