How to avoid null checking in Java?

后端 未结 30 3278
失恋的感觉
失恋的感觉 2020-11-21 04:43

I use object != null a lot to avoid NullPointerException.

Is there a good alternative to this?

For example I often use:



        
相关标签:
30条回答
  • 2020-11-21 05:39

    Ultimately, the only way to completely solve this problem is by using a different programming language:

    • In Objective-C, you can do the equivalent of invoking a method on nil, and absolutely nothing will happen. This makes most null checks unnecessary, but it can make errors much harder to diagnose.
    • In Nice, a Java-derived language, there are two versions of all types: a potentially-null version and a not-null version. You can only invoke methods on not-null types. Potentially-null types can be converted to not-null types through explicit checking for null. This makes it much easier to know where null checks are necessary and where they aren't.
    0 讨论(0)
  • 2020-11-21 05:40

    Only for this situation -

    Not checking if a variable is null before invoking an equals method (a string compare example below):

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

    will result in a NullPointerException if foo doesn't exist.

    You can avoid that if you compare your Strings like this:

    if ( "bar".equals(foo) ) {
     // ...
    }
    
    0 讨论(0)
  • 2020-11-21 05:40

    This is a very common problem for every Java developer. So there is official support in Java 8 to address these issues without cluttered code.

    Java 8 has introduced java.util.Optional<T>. It is a container that may or may not hold a non-null value. Java 8 has given a safer way to handle an object whose value may be null in some of the cases. It is inspired from the ideas of Haskell and Scala.

    In a nutshell, the Optional class includes methods to explicitly deal with the cases where a value is present or absent. However, the advantage compared to null references is that the Optional<T> class forces you to think about the case when the value is not present. As a consequence, you can prevent unintended null pointer exceptions.

    In above example we have a home service factory that returns a handle to multiple appliances available in the home. But these services may or may not be available/functional; it means it may result in a NullPointerException. Instead of adding a null if condition before using any service, let's wrap it in to Optional<Service>.

    WRAPPING TO OPTION<T>

    Let's consider a method to get a reference of a service from a factory. Instead of returning the service reference, wrap it with Optional. It lets the API user know that the returned service may or may not available/functional, use defensively

    public Optional<Service> getRefrigertorControl() {
          Service s = new  RefrigeratorService();
           //...
          return Optional.ofNullable(s);
       }
    

    As you see Optional.ofNullable() provides an easy way to get the reference wrapped. There are another ways to get the reference of Optional, either Optional.empty() & Optional.of(). One for returning an empty object instead of retuning null and the other to wrap a non-nullable object, respectively.

    SO HOW EXACTLY IT HELPS TO AVOID A NULL CHECK?

    Once you have wrapped a reference object, Optional provides many useful methods to invoke methods on a wrapped reference without NPE.

    Optional ref = homeServices.getRefrigertorControl();
    ref.ifPresent(HomeServices::switchItOn);
    

    Optional.ifPresent invokes the given Consumer with a reference if it is a non-null value. Otherwise, it does nothing.

    @FunctionalInterface
    public interface Consumer<T>
    

    Represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects. It is so clean and easy to understand. In the above code example, HomeService.switchOn(Service) gets invoked if the Optional holding reference is non-null.

    We use the ternary operator very often for checking null condition and return an alternative value or default value. Optional provides another way to handle the same condition without checking null. Optional.orElse(defaultObj) returns defaultObj if the Optional has a null value. Let's use this in our sample code:

    public static Optional<HomeServices> get() {
        service = Optional.of(service.orElse(new HomeServices()));
        return service;
    }
    

    Now HomeServices.get() does same thing, but in a better way. It checks whether the service is already initialized of not. If it is then return the same or create a new New service. Optional<T>.orElse(T) helps to return a default value.

    Finally, here is our NPE as well as null check-free code:

    import java.util.Optional;
    public class HomeServices {
        private static final int NOW = 0;
        private static Optional<HomeServices> service;
    
    public static Optional<HomeServices> get() {
        service = Optional.of(service.orElse(new HomeServices()));
        return service;
    }
    
    public Optional<Service> getRefrigertorControl() {
        Service s = new  RefrigeratorService();
        //...
        return Optional.ofNullable(s);
    }
    
    public static void main(String[] args) {
        /* Get Home Services handle */
        Optional<HomeServices> homeServices = HomeServices.get();
        if(homeServices != null) {
            Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
            refrigertorControl.ifPresent(HomeServices::switchItOn);
        }
    }
    
    public static void switchItOn(Service s){
             //...
        }
    }
    

    The complete post is NPE as well as Null check-free code … Really?.

    0 讨论(0)
  • 2020-11-21 05:40

    I've tried the NullObjectPattern but for me is not always the best way to go. There are sometimes when a "no action" is not appropiate.

    NullPointerException is a Runtime exception that means it's developers fault and with enough experience it tells you exactly where is the error.

    Now to the answer:

    Try to make all your attributes and its accessors as private as possible or avoid to expose them to the clients at all. You can have the argument values in the constructor of course, but by reducing the scope you don't let the client class pass an invalid value. If you need to modify the values, you can always create a new object. You check the values in the constructor only once and in the rest of the methods you can be almost sure that the values are not null.

    Of course, experience is the better way to understand and apply this suggestion.

    Byte!

    0 讨论(0)
  • 2020-11-21 05:41

    May I answer it more generally!

    We usually face this issue when the methods get the parameters in the way we not expected (bad method call is programmer's fault). For example: you expect to get an object, instead you get a null. You expect to get an String with at least one character, instead you get an empty String ...

    So there is no difference between:

    if(object == null){
       //you called my method badly!
    

    }

    or

    if(str.length() == 0){
       //you called my method badly again!
    }
    

    They both want to make sure that we received valid parameters, before we do any other functions.

    As mentioned in some other answers, to avoid above problems you can follow the Design by contract pattern. Please see http://en.wikipedia.org/wiki/Design_by_contract.

    To implement this pattern in java, you can use core java annotations like javax.annotation.NotNull or use more sophisticated libraries like Hibernate Validator.

    Just a sample:

    getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)
    

    Now you can safely develop the core function of your method without needing to check input parameters, they guard your methods from unexpected parameters.

    You can go a step further and make sure that only valid pojos could be created in your application. (sample from hibernate validator site)

    public class Car {
    
       @NotNull
       private String manufacturer;
    
       @NotNull
       @Size(min = 2, max = 14)
       private String licensePlate;
    
       @Min(2)
       private int seatCount;
    
       // ...
    }
    
    0 讨论(0)
  • 2020-11-21 05:42

    Asking that question points out that you may be interested in error handling strategies. How and where to handle errors is a pervasive architectural question. There are several ways to do this.

    My favorite: allow the Exceptions to ripple through - catch them at the 'main loop' or in some other function with the appropriate responsibilities. Checking for error conditions and handling them appropriately can be seen as a specialized responsibility.

    Sure do have a look at Aspect Oriented Programming, too - they have neat ways to insert if( o == null ) handleNull() into your bytecode.

    0 讨论(0)
提交回复
热议问题