Pertanyaan Menghindari! = Pernyataan null


saya menggunakan object != null banyak hal yang harus dihindari NullPointerException.

Apakah ada alternatif yang bagus untuk ini?

Sebagai contoh:

if (someobject != null) {
    someobject.doCalc();
}

Ini menghindarkan a NullPointerException, ketika tidak diketahui apakah objek tersebut null atau tidak.

Perhatikan bahwa jawaban yang diterima mungkin sudah ketinggalan zaman, lihat https://stackoverflow.com/a/2386013/12943 untuk pendekatan yang lebih baru.


3548


asal


Jawaban:


Bagi saya ini terdengar seperti masalah umum yang dihadapi oleh para pengembang junior sampai menengah pada suatu titik: mereka entah tidak tahu atau tidak mempercayai kontrak-kontrak yang mereka ikuti dan secara defensif overcheck untuk nol. Selain itu, ketika menulis kode mereka sendiri, mereka cenderung bergantung pada nulls kembali untuk menunjukkan sesuatu sehingga membutuhkan pemanggil untuk memeriksa nol.

Untuk menempatkan ini dengan cara lain, ada dua contoh di mana pemeriksaan nol muncul:

  1. Di mana nol adalah tanggapan yang valid dalam hal kontrak; dan

  2. Dimana itu bukan respon yang valid.

(2) mudah. Entah digunakan assert pernyataan (pernyataan) atau memungkinkan kegagalan (misalnya, NullPointerException). Asersi adalah fitur Java yang sangat kurang dimanfaatkan yang ditambahkan dalam versi 1.4. Sintaksnya adalah:

assert <condition>

atau

assert <condition> : <object>

dimana <condition> adalah ekspresi boolean dan <object> adalah objek yang toString() output metode akan dimasukkan dalam kesalahan.

Sebuah assert pernyataan melempar Error (AssertionError) jika kondisinya tidak benar. Secara default, Java mengabaikan asersi. Anda dapat mengaktifkan pernyataan dengan meneruskan opsi -ea ke JVM. Anda dapat mengaktifkan dan menonaktifkan asersi untuk masing-masing kelas dan paket. Ini berarti Anda dapat memvalidasi kode dengan pernyataan saat mengembangkan dan menguji, dan menonaktifkannya dalam lingkungan produksi, meskipun pengujian saya telah menunjukkan di samping tidak ada dampak kinerja dari pernyataan.

Tidak menggunakan pernyataan dalam kasus ini adalah OK karena kode itu akan gagal, yang akan terjadi jika Anda menggunakan pernyataan. Satu-satunya perbedaan adalah bahwa dengan pernyataan itu mungkin terjadi lebih cepat, dengan cara yang lebih berarti dan mungkin dengan informasi tambahan, yang dapat membantu Anda untuk mencari tahu mengapa itu terjadi jika Anda tidak mengharapkannya.

(1) sedikit lebih keras. Jika Anda tidak memiliki kendali atas kode yang Anda panggil maka Anda terjebak. Jika null adalah respons yang valid, Anda harus memeriksanya.

Jika itu kode yang Anda kendalikan, bagaimanapun (dan ini sering terjadi), maka itu adalah cerita yang berbeda. Hindari menggunakan null sebagai tanggapan. Dengan metode yang mengembalikan koleksi, itu mudah: mengembalikan koleksi kosong (atau array), bukan nulls hampir sepanjang waktu.

Dengan non-koleksi mungkin lebih sulit. Pertimbangkan ini sebagai contoh: jika Anda memiliki antarmuka ini:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

di mana Parser mengambil input pengguna mentah dan menemukan sesuatu untuk dilakukan, mungkin jika Anda mengimplementasikan antarmuka baris perintah untuk sesuatu. Sekarang Anda mungkin membuat kontrak yang mengembalikan nol jika tidak ada tindakan yang tepat. Itu memimpin pengecekan nol yang sedang Anda bicarakan.

Solusi alternatif adalah tidak pernah mengembalikan null dan sebagai gantinya gunakan Pola Objek Null:

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

Membandingkan:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

untuk

ParserFactory.getParser().findAction(someInput).doSomething();

yang merupakan desain yang jauh lebih baik karena mengarah ke kode yang lebih ringkas.

Yang mengatakan, mungkin itu sepenuhnya tepat untuk findAction () metode untuk melempar Exception dengan pesan kesalahan yang berarti - terutama dalam hal ini di mana Anda mengandalkan input pengguna. Akan jauh lebih baik untuk metode findAction untuk membuang Exception daripada metode pemanggilan untuk meledakkan NullPointerException sederhana tanpa penjelasan.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

Atau jika Anda berpikir bahwa mekanisme try / catch terlalu buruk, daripada Do Nothing, tindakan default Anda harus memberikan umpan balik kepada pengguna.

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

2389



Jika Anda menggunakan (atau berencana untuk menggunakan) IDE Java seperti JetBrains IntelliJ IDEA, Eclipse atau Netbeans atau alat seperti findbugs, maka Anda dapat menggunakan anotasi untuk memecahkan masalah ini.

Pada dasarnya, Anda punya @Nullable dan @NotNull.

Anda dapat menggunakan dalam metode dan parameter, seperti ini:

@NotNull public static String helloWorld() {
    return "Hello World";
}

atau

@Nullable public static String helloWorld() {
    return "Hello World";
}

Contoh kedua tidak akan dikompilasi (di IntelliJ IDEA).

Saat Anda menggunakan yang pertama helloWorld() berfungsi dalam potongan kode lain:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

Sekarang compiler IntelliJ IDEA akan memberitahu Anda bahwa cek tidak berguna, karena helloWorld() fungsi tidak akan kembali null, pernah.

Menggunakan parameter

void someMethod(@NotNull someParameter) { }

jika Anda menulis sesuatu seperti:

someMethod(null);

Ini tidak dapat dikompilasi.

Contoh terakhir menggunakan @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Melakukan ini

iWantToDestroyEverything().something();

Dan Anda dapat yakin bahwa ini tidak akan terjadi. :)

Ini cara yang bagus untuk membiarkan compiler memeriksa sesuatu lebih dari biasanya dan menegakkan kontrak Anda menjadi lebih kuat. Sayangnya, itu tidak didukung oleh semua kompiler.

Di IntelliJ IDEA 10.5 dan seterusnya, mereka menambahkan dukungan untuk yang lain @Nullable  @NotNull implementasi.

Lihat posting blog Lebih fleksibel dan dapat dikonfigurasi @ Nullable / @ NotNull anotasi.


516



Jika nilai-nol tidak diizinkan

Jika metode Anda dipanggil secara eksternal, mulailah dengan sesuatu seperti ini:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

Kemudian, di sisa metode itu, Anda akan tahu itu object bukan nol.

Jika itu adalah metode internal (bukan bagian dari API), hanya dokumen yang tidak boleh null, dan hanya itu.

Contoh:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

Namun, jika metode Anda hanya melewatkan nilai pada, dan metode berikutnya meneruskannya pada dll, itu bisa menjadi masalah. Dalam hal ini Anda mungkin ingin memeriksa argumen seperti di atas.

Jika nol diizinkan

Ini sangat tergantung. Jika menemukan bahwa saya sering melakukan sesuatu seperti ini:

if (object == null) {
  // something
} else {
  // something else
}

Jadi saya cabang, dan melakukan dua hal yang sama sekali berbeda. Tidak ada potongan kode yang jelek, karena saya benar-benar perlu melakukan dua hal yang berbeda tergantung pada data. Misalnya, haruskah saya bekerja pada input, atau haruskah saya menghitung nilai default yang bagus?


Sebenarnya jarang saya menggunakan idiom "if (object != null && ...".

Mungkin lebih mudah memberi Anda contoh, jika Anda menunjukkan contoh di mana Anda biasanya menggunakan idiom.


282



Wow, saya hampir benci untuk menambahkan jawaban lain ketika kami memiliki 57 cara berbeda untuk merekomendasikan NullObject pattern, tapi saya pikir beberapa orang yang tertarik dengan pertanyaan ini mungkin ingin tahu bahwa ada proposal di atas meja untuk Java 7 untuk ditambahkan "penanganan tidak aman"—sebuah syntax streamline untuk logika if-not-equal-null.

Contoh yang diberikan oleh Alex Miller terlihat seperti ini:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

Itu ?. berarti hanya de-referensi identifier kiri jika tidak null, jika tidak mengevaluasi sisa dari ekspresi sebagai null. Beberapa orang, seperti anggota Java Posse, Dick Wall dan pemilih di Devoxx benar-benar menyukai proposal ini, tetapi ada juga pertentangan, dengan alasan bahwa itu akan benar-benar mendorong lebih banyak penggunaan null sebagai nilai sentinel.


Memperbarui: Sebuah proposal resmi untuk operator nol-aman di Java 7 telah dikirimkan di bawah Koin Proyek. Sintaksnya sedikit berbeda dari contoh di atas, tetapi itu adalah gagasan yang sama.


Memperbarui: Proposal operator tidak aman tidak berhasil masuk ke dalam Project Coin. Jadi, Anda tidak akan melihat sintaks ini di Java 7.


215



Jika nilai yang tidak terdefinisi tidak diizinkan:

Anda mungkin mengkonfigurasi IDE Anda untuk memperingatkan Anda tentang potensi dereferencing null. Misalnya. di Eclipse, lihat Preferences> Java> Compiler> Errors / Warnings / Null analysis.

Jika nilai yang tidak terdefinisi diizinkan:

Jika Anda ingin mendefinisikan API baru di mana nilai yang tidak jelas masuk akal, menggunakan Pola Opsi (mungkin familier dari bahasa fungsional). Ini memiliki keuntungan sebagai berikut:

  • Dinyatakan secara eksplisit di API apakah input atau output ada atau tidak.
  • Kompilator memaksa Anda untuk menangani kasus "tidak terdefinisi".
  • Option adalah monad, jadi tidak perlu pemeriksaan verbose null, cukup gunakan peta / foreach / getOrElse atau kombinator serupa untuk menggunakan nilai dengan aman (contoh).

Java 8 memiliki built-in Optional kelas (disarankan); untuk versi sebelumnya, ada alternatif perpustakaan, misalnya Jambu biji's Optional atau FunctionalJava's Option. Tetapi seperti banyak pola gaya fungsional, menggunakan Opsi di Java (bahkan 8) menghasilkan cukup banyak boilerplate, yang dapat Anda kurangi menggunakan bahasa JVM yang lebih sedikit verbose, mis. Scala atau Xtend.

Jika Anda harus berurusan dengan API yang mungkin mengembalikan null, Anda tidak bisa berbuat banyak di Jawa. Xtend dan Groovy memiliki Operator Elvis  ?: dan operator dereference tidak aman  ?., tetapi perhatikan bahwa ini mengembalikan null dalam kasus referensi nol, sehingga hanya "menghalangi" penanganan yang tepat dari null.


177



Hanya untuk situasi ini -

Tidak memeriksa apakah variabel null sebelum menerapkan metode yang sama (string membandingkan contoh di bawah):

if ( foo.equals("bar") ) {
 // ...
}

akan menghasilkan a NullPointerException jika foo tidak ada.

Anda dapat menghindari itu jika Anda membandingkan Anda Stringseperti ini:

if ( "bar".equals(foo) ) {
 // ...
}

160



Dengan Java 8 datang yang baru java.util.Optional kelas yang bisa dibilang memecahkan beberapa masalah. Setidaknya bisa dikatakan bahwa itu meningkatkan keterbacaan kode, dan dalam kasus API publik membuat kontrak API lebih jelas ke pengembang klien.

Mereka bekerja seperti itu:

Objek opsional untuk tipe tertentu (Fruit) dibuat sebagai jenis kembalinya suatu metode. Ini bisa kosong atau mengandung Fruit obyek:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Sekarang lihat kode ini di mana kami mencari daftar Fruit (fruits) untuk contoh Buah yang diberikan:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

Anda dapat menggunakan map() operator untuk melakukan komputasi pada - atau mengekstrak nilai dari - objek opsional. orElse() memungkinkan Anda memberikan fallback untuk nilai yang hilang.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

Tentu saja, pemeriksaan untuk nilai nol / kosong masih diperlukan, tetapi setidaknya pengembang sadar bahwa nilainya mungkin kosong dan risiko lupa untuk memeriksa terbatas.

Dalam API yang dibangun dari awal menggunakan Optional setiap kali nilai kembali mungkin kosong, dan mengembalikan objek biasa hanya ketika tidak bisa null (konvensi), kode klien mungkin meninggalkan pemeriksaan nol pada nilai pengembalian objek sederhana ...

Tentu saja Optional bisa juga digunakan sebagai argumen metode, mungkin cara yang lebih baik untuk menunjukkan argumen opsional daripada 5 atau 10 metode kelebihan muatan dalam beberapa kasus.

Optional menawarkan metode lain yang mudah digunakan, seperti orElse yang memungkinkan penggunaan nilai default, dan ifPresent yang bekerja dengan ekspresi lambda.

Saya mengundang Anda untuk membaca artikel ini (sumber utama saya untuk menulis jawaban ini) di mana NullPointerException (dan secara umum null pointer) bermasalah serta (sebagian) solusi yang dibawa oleh Optional dijelaskan dengan baik: Java Opsional Objek.


135



Tergantung pada jenis objek apa yang Anda periksa, Anda mungkin dapat menggunakan beberapa kelas di commach apache seperti: apache commons lang dan koleksi apache commons

Contoh:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

atau (tergantung pada apa yang perlu Anda periksa):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

Kelas StringUtils hanya satu dari sekian banyak; ada beberapa kelas yang baik di commons yang melakukan manipulasi yang aman.

Berikut mengikuti contoh bagaimana Anda dapat menggunakan null vallidation di JAVA ketika Anda memasukkan pustaka apache (commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

Dan jika Anda menggunakan Spring, Spring juga memiliki fungsi yang sama dalam paketnya, lihat pustaka (spring-2.4.6.jar)

Contoh tentang cara menggunakan classf statis ini dari spring (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

113



  • Jika Anda menganggap suatu objek tidak boleh null (atau itu bug) gunakan pernyataan.
  • Jika metode Anda tidak menerima null params, katakanlah dalam javadoc dan gunakan pernyataan.

Anda harus memeriksa objek! = Null hanya jika Anda ingin menangani kasus di mana objek mungkin ...

Ada proposal untuk menambahkan anotasi baru di Java7 untuk membantu dengan parath null / notnull: http://tech.puredanger.com/java7/#jsr308


87



Saya penggemar kode "gagal cepat". Tanyakan pada diri Anda - apakah Anda melakukan sesuatu yang berguna dalam kasus di mana parameternya nol? Jika Anda tidak memiliki jawaban yang jelas tentang apa yang harus dilakukan kode Anda dalam kasus itu ... I.e. seharusnya tidak pernah null di tempat pertama, lalu abaikan dan biarkan NullPointerException dilemparkan. Kode panggilan akan membuat sama banyak rasa NPE karena akan menjadi IllegalArgumentException, tetapi akan lebih mudah bagi pengembang untuk debug dan memahami apa yang salah jika NPE dilemparkan daripada kode Anda mencoba untuk menjalankan beberapa kemungkinan tak terduga lainnya logika - yang pada akhirnya menghasilkan aplikasi gagal pula.


80



Terkadang, Anda memiliki metode yang beroperasi pada parameternya yang mendefinisikan operasi simetris:

a.f(b); <-> b.f(a);

Jika Anda tahu b tidak pernah bisa null, Anda bisa menukarnya. Ini sangat berguna untuk yang setara: Dari pada foo.equals("bar"); lebih baik lakukan "bar".equals(foo);.


69