简体   繁体   中英

Variable referencing vs. repetitive get calls to avoid null pointers

I have a situation in my code where I make about 5 chained get calls, and any of those get calls could return a null value. I wish it didn't have to be that way, but that's just how the service I'm consuming returns the object I request, so I have to deal with it.

Originally, my code looked something like this:

String firstDomain = book.getBookImages().getDomains().getDefaults().getDomain().get(0);

Unfortunately, that line is prone to null pointers and arraylist out of bounds exceptions. I know I'm going to have to check for null pointers, but I'm trying to decide

A. The most efficient code to do so

and

B. The best looking code to do so

One option is to assign a ton of references and then check for nulls. Something like this:

BookImages bImages = book.getBookImages();
Domains domains = null;
Defaults defaults = null;
List<String> domain = null;
String firstDomain = null;
if (bImages != null) {
    domains = bImages.getDomains();
    if (domains != null) {
        defaults = domains.getDefaults();
        if (defaults != null) {
            domain = defaults.getDomain();
            if (domain != null && domain.size() > 0) {
                firstDomain = domain.get(0);
            }
        }
    }
}
if (firstDomain == null) {
    throw new IncompleteBookException("The book was incompletely attributed.");
}

I think that's pretty efficient, but it bothers me how many lines it is. It more than doubles the length of the method it is a part of.

This is the other alternative I can think of:

if (book.getBookImages() == null || book.getBookImages().getDomains() == null || book.getBookImages().getDomains().getDefaults() == null || book.getBookImages().getDomains().getDefaults().getDomain() == null || book.getBookImages().getDomains().getDefaults().getDomain().size() < 1 || book.getBookImages().getDomains().getDefaults().getDomain().get(0) == null) {
    throw new IncompleteBookException("The book was incompletely attributed.");
} 

I like the fact that that's only three lines, even if one is pretty darn ridiculous, but I'm not sure if Java's runtime or compiler would optimize away those repetitive method calls.

I'm also open to other, better solutions. Does anyone know if one or the other of those options would perform better than the other, or is this such a micro-optimization that it's foolish to even bother thinking about it, and I should just use whichever looks nicer to me? I may have to do this many times for different things.

To follow your second approach, you can apply this:

    if ((bImages=book.getBookImages()) == null
            || (domains=bImages.getDomains()) == null
            || (defaults=domains.getDefaults()) == null
            || (domain=defaults.getDomain()) == null
            || domain.size() < 1
            || (firstDomain=domain.get(0)) == null) {
        throw new IncompleteBookException("The book was incompletely attributed.");
    } else {
        //here you can use the firstDomain variable, that is set with the correct value
    }

This is better because you avoid multiple (useless) identical calls, and you already set the correct value in the variable firstDomain (of course, only if there is nothing null and so on...)

You should not worry about the length of the code too much if the longer code is correct and shorter incorrect. The goal of programming is not writing the shortest possible code (with the exception of some competitions, etc).

Your first (longer) way is correct, even if a bit longish.

The second way will make repeated calls to the same method. This is something you probably want to avoid - it may have undesirable effects and is worse for performance.

As Szymon pointed out you should not call same method twice.

To make your original solution slightly more readable by combining all your nested null checks into one:

        BookImages bImages = null;
        Domains domains = null;
        Defaults defaults = null;
        List<String> domain = null;
        String firstDomain = null;

        if ((bImages = book.getBookImages()) != null
                && (domains = bImages.getDomains()) != null
                && (defaults = domains.getDefaults()) != null
                && (domain = defaults.getDomain() != null
                && domain.size() > 0) {

                        firstDomain = domain.get(0);

        }

Try using a boolean method; they tend to make logic a lot simpler and much more legible; for example, I would do this:

public boolean isCompletelyAttributed(BookImages bImages) {
    if (bImages == null) return false;
    if (bImages.getDomains() == null) return false;
    if (bImages.getDomains().getDefaults() == null) return false;
    if (bImages.getDomains().getDefaults().getDomains() == null) return false;

    return bImages.getDomains().getDefaults.getDomains.size() > 0;
}

Then you call

if (!isCompletelyAttributed(book.getImages())) {
    throw new IncompleteBookException("The book was incompletely attributed.");
}

I also recommend that you use Nicola or kiruwka's solution in your boolean method. I was not aware that you could reassign variables while doing logic on them, but that appears to be an elegant solution.

Here's a short-ish way to do it. But hey, I don't know the specifics of what you're building and whether this works for you.

try {
    String firstDomain = book.getBookImages().getDomains().getDefaults().getDomain().get(0);
    // operate on firstDomain
} catch (NullPointerException e) {
    throw new IncompleteBookException("The book was incompletely attributed.");
} catch (IndexOutOfBoundsException e) {
    throw new IncompleteBookException("The book was incompletely attributed.");
}

I've heard that people are concerned with the performance of something like this, because the VM will have to fill in a stack trace for the NullPointerException . In this case, you'll already be doing a comparable operation for the IncompleteBookException anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM