Boolean.valueOf () terkadang menghasilkan NullPointerException

115

Saya memiliki kode ini:

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

Masalah saya adalah saya tidak mengerti mengapa Tes 3 berfungsi dengan baik (mencetak falsedan tidak menghasilkan NullPointerException) sementara Tes 4 melempar NullPointerException. Seperti yang Anda lihat dalam tes 1 dan 2 , nulldan modifiedItems.get("item1")adalah sama dengan dan null.

Perilakunya sama di Java 7 dan 8.

David E
sumber
modifiedItems.get ("item1") ini null, Anda menyadarinya, tetapi Anda berasumsi bahwa meneruskan ini ke valueOf tidak akan berakhir di NPE?
Stultuske
16
@Stultuske: Ini adalah pertanyaan yang valid, mengingat hanya dua baris di atas yang meneruskan literal nullke fungsi yang sama tidak menghasilkan NPE! Ada alasan bagus untuk itu, tapi itu pasti membingungkan pada pandangan pertama :-)
psmears
25
Saya terkesan. Ini adalah pertanyaan pengecualian pointer nol paling menarik yang pernah saya lihat selama bertahun-tahun.
candied_orange
@ Jeroen ini bukan penipu dari pertanyaan itu . Meskipun benar bahwa membuka kotak adalah hal biasa untuk kedua masalah tersebut, tidak ada perbandingan yang terjadi di sini. Hal utama tentang pertanyaan ini adalah bahwa hal itu terjadi karena cara mengatasi beban berlebih; dan itu hal yang sangat berbeda dari cara ==penerapannya.
Andy Turner

Jawaban:

178

Anda harus melihat dengan cermat overload mana yang dipanggil:

  • Boolean.valueOf(null)sedang memohon Boolean.valueOf(String). Ini tidak memunculkan NPEbahkan jika disediakan dengan parameter null.
  • Boolean.valueOf(modifiedItems.get("item1"))dipanggil Boolean.valueOf(boolean), karena modifiedItemsnilainya bertipe Boolean, yang memerlukan konversi unboxing. Karena modifiedItems.get("item1")itu null, unboxing dari nilai itu - bukan Boolean.valueOf(...)- yang melempar NPE.

Aturan untuk menentukan kelebihan beban yang dipanggil cukup rumit , tetapi secara kasar aturannya seperti ini:

  • Dalam lintasan pertama, metode yang cocok dicari tanpa mengizinkan tinju / unboxing (atau metode arity variabel).

    • Karena nulladalah nilai yang dapat diterima untuk a Stringtetapi tidak boolean, Boolean.valueOf(null)dicocokkan dengan Boolean.valueOf(String)dalam umpan ini;
    • Booleantidak dapat diterima untuk salah satu Boolean.valueOf(String)atau Boolean.valueOf(boolean), jadi tidak ada metode yang cocok untuk pass ini Boolean.valueOf(modifiedItems.get("item1")).
  • Dalam lintasan kedua, metode yang cocok dicari, memungkinkan tinju / unboxing (tetapi masih bukan metode arity variabel).

    • A Booleanbisa di-unbox boolean, jadi Boolean.valueOf(boolean)cocok untuk Boolean.valueOf(modifiedItems.get("item1"))di pass ini; tetapi konversi unboxing harus disisipkan oleh compiler untuk memanggilnya:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (Ada lintasan ketiga yang memungkinkan metode arity variabel, tetapi itu tidak relevan di sini, karena dua lintasan pertama cocok dengan kasus ini)

Andy Turner
sumber
3
Bisakah kode lebih jelas jika kita gunakan Boolean.valueOf(modifiedItems.get("item1").booleanValue())dalam kode sumber daripada Boolean.valueOf(modifiedItems.get("item1"))?
CausingUnderflowsEverywhere
1
@CausingUnderflowsEverywhere tidak benar-benar - sangat sulit untuk melihat yang .booleanValue()terkubur dalam ekspresi. Dua pengamatan: 1) auto (un) boxing adalah fitur Java yang disengaja untuk menghilangkan celah sintaksis; melakukannya sendiri adalah mungkin, tetapi tidak idiomatis; 2) ini tidak membantu Anda sama sekali - ini tentu saja tidak menghentikan terjadinya masalah, juga tidak memberikan info tambahan saat kegagalan terjadi (jejak tumpukan akan identik, karena kode yang dieksekusi identik).
Andy Turner
@CausingUnderflowsEverywhere lebih baik menggunakan perkakas untuk menyoroti masalah, misalnya intellij akan memberi Anda informasi tentang potensi NPE di sini.
Andy Turner
13

Karena modifiedItems.getmengembalikan a Boolean(yang tidak dapat di- cast ke a String), tanda tangan yang akan digunakan adalah Boolean.valueOf(boolean), di mana Booleanoutboxnya menjadi primitif boolean. Setelah nulldikembalikan sana, outboxing gagal dengan NullPointerException.

Mureinik
sumber
11

Tanda tangan metode

Metode ini Boolean.valueOf(...)memiliki dua tanda tangan:

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

modifiedItemsNilai Anda adalah Boolean. Anda tidak dapat melemparkan Booleanke Stringsehingga akibatnya tanda tangan pertama akan dipilih

Unboxing Boolean

Dalam pernyataan Anda

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

yang bisa dibaca sebagai

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

Namun, pada dasarnya modifiedItems.get("item1")kembali nulljadi Anda akan memilikinya

null.booleanValue()

yang jelas mengarah ke a NullPointerException

Al-un
sumber
Kata-kata yang salah, terima kasih telah menunjuk dan jawaban diperbarui mengikuti umpan balik Anda. Maaf, saya belum melihat jawaban Anda saat menulis dan saya melihat jawaban saya terlihat seperti jawaban Anda. Haruskah saya menghapus jawaban saya untuk menghindari kebingungan untuk OP?
Al-un
4
Jangan hapus di akun saya. Ingat, ini bukan permainan zero-sum: orang dapat (dan memang) memberi suara positif pada banyak jawaban.
Andy Turner
3

Seperti yang sudah dijelaskan Andy dengan sangat baik alasan dari NullPointerException:

yang disebabkan oleh Boolean un-boxing:

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

diubah menjadi:

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

saat runtime dan kemudian membuang NullPointerExceptionjika modifiedItems.get("item1")null.

Sekarang saya ingin menambahkan satu poin lagi di sini bahwa un-boxing dari kelas-kelas berikut ke primitif masing-masing juga dapat menghasilkan NullPointerExceptionpengecualian jika objek yang dikembalikan sesuai nol.

  1. byte - Byte
  2. char - Karakter
  3. float - Float
  4. int - Integer
  5. Panjang panjang
  6. pendek - Pendek
  7. ganda - Ganda

Ini kodenya:

    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
Mohit Tyagi
sumber
1
"Diubah menjadi ... saat runtime" diubah menjadi itu pada waktu kompilasi.
Andy Turner
0

Sebuah cara untuk memahaminya adalah ketika Boolean.valueOf(null)dipanggil, java dengan tepat diberitahu untuk mengevaluasi null.

Namun, ketika Boolean.valueOf(modifiedItems.get("item1"))dipanggil, java diberitahu untuk mendapatkan nilai dari HashTable tipe objek Boolean, tetapi ia tidak menemukan tipe Boolean, ia malah menemukan jalan buntu (null) meskipun ia mengharapkan Boolean. Pengecualian NullPointerException dilemparkan karena pembuat bagian java ini memutuskan bahwa situasi ini adalah contoh dari sesuatu yang salah dalam program yang memerlukan perhatian programmer. (Sesuatu yang tidak diinginkan terjadi.)

Dalam hal ini lebih banyak perbedaan antara sengaja menyatakan bahwa Anda menginginkan null ada di sana, dan java menemukan referensi yang hilang ke objek (null) di mana objek itu dimaksudkan untuk ditemukan.

Lihat informasi selengkapnya tentang NullPointerException di jawaban ini: https://stackoverflow.com/a/25721181/4425643

CausingUnderflowsEverywhere
sumber
Jika seseorang dapat membantu meningkatkan jawaban ini, saya sedang memikirkan sebuah kata yang merujuk pada pemrogram yang menulis sesuatu dengan maksud yang jelas, tanpa ambiguitas
CausingUnderflowsEverywhere