1

In Java, objects are, by default, initialized to null.

I recently came across this answer while doing research into handling NPE properly: https://softwareengineering.stackexchange.com/a/187225

The following is my primary take-away from the post:

If you never set a variable to null you can never have an unexpected null.

Don't allow null parameters

I've been wondering how to handle this in Java and Android. Many of the crash reports I see on the developer dashboard are NPE, and I've noticed that in my code, I use null checking to see if I can continue with an operation.

I'm combing through my codebase trying to remove null and default instantiation as much as possible, but I'm running into a few areas where this appears impossible and I'm not sure how to fix the errors which arise from these variables.

For example, when using the Google Maps API, you must declare a GoogleMap object. The way I currently have it set up is to declare the variable as a member of my Activity, then initialize it during onCreate(). Unfortunately, during onResume() when I call .clear() on the GoogleMap, occasionally the field will be null. I'm unable to reproduce this error in my own environment, which led me to do more reading into handling NPE.

In another (very popular) question here: Avoiding != null statements, The top and accepted answer recommends three strategies for resolving this issue.

  1. The assert statement. On the surface, this looks more like unit-testing/debug code, especially since Java ignores assert by default.
  2. The Null Object pattern. This is probably the most appealing of the solutions presented as it ends up looking and feeling cleaner. However, when working with external libraries, I'm not sure that this is an option. Extending an external library seems messy.
  3. The try/catch. This is just difficult. Since null is so pervasive in Java, the code will end up densely peppered with try/catch for everything that could be null, or end up having swaths of code blocked off, which catching errors in won't allow much precision in handling the aftermath.

My question is, is there another option? Perhaps a coding paradigm I can use or some kind of built-in that might fix this?

Optimally, I would like a solution where I don't crash, lose data reliability, or lose application functionality.

Note - This is not a question about problems with my code, this is a question about how to defensively write code to prevent unexpected NPE in a language where the default initialization is null.

Community
  • 1
  • 1
nukeforum
  • 1,284
  • 3
  • 16
  • 38
  • Also check the [Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) "construct" introduced (finally) in Java 8 – x80486 Sep 08 '15 at 17:41
  • Why clearing a map would make it null ? @ɐuıɥɔɐɯ not an expert of Android, but not sure you can use this in the Android SDK – Dici Sep 08 '15 at 17:42
  • Clearing the map doesn't make it null. For some reason, occasionally the map *already is null* when the `.clear()` statement is reached. – nukeforum Sep 08 '15 at 17:45
  • @ɐuıɥɔɐɯ Features exclusive to Java 8 are not available in Android as far as I'm aware, but it is good to know that they've engineered a solution, considering how embedded `null` is in the language. – nukeforum Sep 08 '15 at 17:48
  • Though you're asking not to mention problems of your code but the problem is exactly in your code. There is no magic method or methodology which eliminates all `NPE`. And by the way: you may wrongly retrieve `GoogleMap` instance. I hope you know that `getMap` may return null. There is `getMapAsync` method which should be called in this case. – eleven Sep 08 '15 at 18:12
  • @Foxinsocks Thank you for the insight. I wasn't aware that this was the case, however, that particular example is only one part of a larger code base that is riddled(slight hyperbole) with these kinds of problems. – nukeforum Sep 08 '15 at 18:20

4 Answers4

1

I really don't see why null checking is considered as something bad, as for preventing top level exceptions use @NonNull and @Nullable annotations, static analysis really helps.

dtx12
  • 4,438
  • 1
  • 16
  • 12
  • This helps a lot with compile-time `null` issues, but unfortunately doesn't help with preventing runtime issues like I described in my example. – nukeforum Sep 08 '15 at 17:53
  • Most of classes from android sdk have methods which marked as nullable and vice versa, shouldn't be a big deal so. But if you're working with another's code, there's no easy solution except detecting it at runtime. I suggest that you won't check everything for null, because it's a bit of overhead. – dtx12 Sep 08 '15 at 17:58
0

I find exception to the premise of the referenced article. It states:

the bug isn't where the NullPointerException was thrown, the bug is earlier in your code, where a variable was set to null without you expecting it to be.

I'd say the bug is you not expecting the value to be null, so the bug is exactly where the NPE occurs.

Like any other tool, it's how it is used that matters most. Using a null value to indicate that there is no value is perfectly correct, as long as it is well-defined.

That said, it is true that null is overused, and should sometimes be replaced with an exception. But it all depends on circumstances.

Look at the BlockingQueue interface in Java. To remove a value from the queue, you can call remove() which throws exception if queue is empty, or you can call poll() which returns null. If you are expecting a value, use remove(). If you know the queue might be empty, and that you will handle that, use poll(). Don't use remove() with try-catch for flow control.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Interesting interpretation. Do you not consider avoiding the `null` state entirely a solution to the bug of not expecting the value to be `null`? – nukeforum Sep 08 '15 at 18:38
  • @nukeforum If the return value can be "special", whether "null" or "empty", you may still need to handle it. A special value should always be well documented and caller would likely always need to handle it differently. An empty object prevents NPE, but it doesn't guarantee that your code handles the special value correctly. In fact it might have the opposite effect of appearing to work when it doesn't, without error. Using the Java 8 `Optional`, or a similar pre-8 construct, makes an explicit distinction between "normal" and "special", and caller *must* act accordingly. – Andreas Sep 08 '15 at 18:49
  • >If the return value can be "special", whether "null" or "empty", you may still need to handle it. That's exactly what I've been running up against. I'm trying to avoiding checking for `null` everywhere, but unless I check, all I can do is swallow an NPE or suffer data loss through an empty or Null Object. `BlockingQueue` looks interesting, but I think I may just fall back on `!=null`. – nukeforum Sep 08 '15 at 18:56
  • @nukeforum It should always be part of the contract (definition) of a method whether it can return null. If it can, then all callers must check for it. A construct like `Optional` (e.g. Google's Guava has one for pre-Java 8 use) enforces such checking, but that is not technically necessary. If is always up to the caller to handle all *valid* values returned from a method. If the coder bothers to read the javadoc, you'll never get an NPE. – Andreas Sep 08 '15 at 19:11
0

I asked the same question to myself and end up throwing exception and caching it in the top level of my code. Doing so, i just had to declared the try catch once with no condition. Of course, it requires to assure that if some values are not expected to be there or it's invalid, to throw the exception:

    bool isEquals(string name)
    {
      if(tempData == name.toUpperCase()) // if name is null,a null reference will be throw

       //...
    }

Even though you don't get rid of NPE and try/catch, you can be confidence where did the app failed and that's is a big plus.

Misters
  • 1,337
  • 2
  • 16
  • 29
0

Null, in and of itself, is not something that can be avoided. Any declared reference that has yet to be assigned, and successfully compiled, is considered null in Java for very good reason: there are trash values on the heap. Java keeps track of your assignments and returns null on memory locations that still contain trash values from when the application was assigned that heap block. It prevents you from getting values for things like integers, thinking they belong to you, when really they are left over from some other application that happened to use that same address before your app started.

Each of the strategies listed have their own uses, and none are a catch-all for dealing with possible NPE's.

Assert is generally something to be avoided. It results in a halt statement if the condition isn't met. In other words, it's not a graceful exit. A !=null statement achieves the same goal as an assert, but with the option to respond to the condition rather than halt the program.

try/catch is powerful in cases where you can run into race conditions. This is especially useful when making calls to asynchronous libraries and libraries that heavily use network dependent operations. GoogleMaps has many methods that fetch updated information based on the new location or view frame and in some cases you may try to make a call against an object that's managed by the library that may be mid-refresh. I usually make the assumption of a race condition when I can't easily reproduce a NPE.

Finally, I don't see any value in trying to avoid a !=null check. It captures and allows you to respond to all conditions that a try/catch is inappropriate for, and costs nothing. All boolean operations are O(1) operations. You have to use a billion or more of them back to back in order to see even 1 second of performance degradation. Not worth the headache to eliminate.

Stephan
  • 666
  • 8
  • 23
  • You first paragraph is flawed. Fields are *defined* to be null for references, it is not a consideration. Variables *cannot* be queried until [definitely-assigned](https://docs.oracle.com/javase/specs/jls/se7/html/jls-16.html), as enforced by the compiler, so there is no need to consider a value before then. – Andreas Sep 08 '15 at 18:16
  • If you run into race conditions, you didn't code your multi-threaded logic correctly. A try-catch is not the answer to that. – Andreas Sep 08 '15 at 18:19
  • Andreas has some really good points about this particular answer, but I feel like the overall theme is correct and runs somewhat parallel to Andreas' answer. @Stephan, can you weigh in on Andreas' comments either in the comments or as an edit to the answer? I would like to accept this answer, but I don't want to mislead a future reader by accepting an answer with potential flaws. – nukeforum Sep 08 '15 at 19:42
  • 1
    Hey guys, sorry it took so long to get back around to this. To @Andreas first point, s/he is absolutely correct if we're talking compile time, however if this is a running application, the variable is definitely-assigned, and so we enter the realm of null-assigned references. To the second point, yes and no. Race conditions for multi-threading does mean you did something wrong, however when it comes to java web applications, race conditions can happen often with asynchronous fetches, even when coded properly. I mentioned that in the original answer. – Stephan Sep 29 '15 at 15:24