Defeat NPE hell in Java without using IntelliJ IDEA

    Denial of responsibility


    Firstly, the material appeared because I wanted to quickly explain to the Indians my colleagues what nullanalysis is, what it is good for and why you need to overcome corporate laziness right now and start using this analysis in projects. Articles containing the entire systematic informatsiiyu about a subject without being tied to the IDE, was not found , so I had to write myself. And although it did not work out as a result of a complete systematization, I still wanted to share the material with a wider audience. There will be a minimum of text and a lot of images.

    Secondly, the resource already has an excellent article tr1cks devoted to IntelliJ IDEA, which reinforces the already strong impression that IDEA is very good, and Eclipse is for the poor(which became a byword). For balance, I will focus on Eclipse.

    I will use FindBugs project annotations (also known as JSR 305 annotations) due to the lack of their binding to a specific development environment (fans can use org.eclipse.jdt.annotation.*and org.jetbrains.annotations.*), and also because they are available in Maven Central. For those using Maven, just add the following to the section :

    com.google.code.findbugsjsr3053.0.0

    Just in case, I make a reservation that

    	@Nonnull
    	public String getName() {
    		// ...
    	}
    

    and

    	public @Nonnull String getName() {
    		// ...
    	}
    

    - these are two absolutely equivalent constructions, although in some cases the two variants of the obtained bytecode can differ by exactly 1 byte (by the way, why do you think?)

    Eclipse


    All of the following has been tested on Eclipse 4.4 (Luna). At the same time, if my memory serves me right, the described functionality was already present in 3.8 (and possibly earlier).

    Customization


    • If you use the M2Eclipse module , annotations are added instantly:


    • Next, you need to get into global settings or project settings, select Java -> Compiler -> Errors / Warnings -> Null analysis and enable Enable annotation-based null analysis .


    • After that, Eclipse will ask whether to raise the level of the Null pointer access and Potential null pointer access problems to “Error”. It should be borne in mind that, by agreeing, you will receive many compilation errors for existing code, so I would recommend that you do this only for new projects, otherwise leave the “Warning” level.
    • Now you can clear the Use default annotations for null specifications flag and select JSR 305 annotations instead of the factory ones:


    • The Enable syntactic null analysis for fields flag should be selected: in this case the number of false positives (and they will be) will decrease.
    • The Missing '@NonNullByDefault' annotation on package flag is also worth choosing (the presence of this flag compares Eclipse with IDEA), but annotating with @NonNullByDefaultor @ParametersAreNonnullByDefaultonly new, new packages (a lot of code already written, including in JDK giblets, is corny does not meet the requirement of "non-zero parameters" and will lead to compilation errors). Just in case, annotations on the whole package (in the file "package-info.java") are "hung" like this:

      @ParametersAreNonnullByDefault
      package com.example;
      import javax.annotation.ParametersAreNonnullByDefault;
      


    False positive diagnosis


    Few people will be happy if the diagnosis is wrong. Fortunately, there is an opportunity to say: “Doctor, you were drunk and analyzed someone else's blood!”

    Problem


    The picture shows that Eclipse is not able to determine that (in a single-threaded scenario, of course), the field fieldcannot be nullat the time of the call hashCode():


    Yes, of course, we can mark the whole method with @SuppressWarnings("null"), but this will nullify all the benefits of null analysis.

    Workaround 0


    Add assert:


    Workaround 1


    Longer, but also possible. Pull the field value into a local variable, mark it as @Nonnullwe will continue to work with it:


    Underwater rocks


    Starting with Java 1.8, a new type of metadata is supported - type annotations (see JSR 308 ) - and accordingly annotations are required to be explicitly specified @Target. FindBugs annotations do not meet this requirement. Therefore, if you use Eclipse up to 4.4 inclusive (Luna) and Java 1.8, then null-analysis using FindBugs annotations will not work ( bug # 435805 ). In general, in this case, it is better to switch to Eclipse 4.5 (Mars).

    Home reading




    IDEA


    IntelliJ IDEA supports null-analysis, it seems, from the beginning of time (checks Constant conditions & exceptions and @ NotNull / @Nullable problems ).




    What is nice, the environment immediately “out of the box” knows about a number of suitable annotations, including JSR 305:


    The only pity is that, unlike Eclipse, annotations on the whole package (as @NonNullByDefaultor @ParametersAreNonnullByDefault) are not supported.

    Update


    Starting with build 138.1372 ( IDEA-125281 ), @ParametersAreNonnullByDefaultand @ParametersAreNullableByDefaultat the package level, they are recognized and analyzed, although they do not appear anywhere in the settings (thanks to Borz ). Moreover, functionality appears to be present in IDEA 13.1.6 (build 135.1306).

    Netbeans


    NetBeans supports null-analysis since 7.3, see next. English article .

    Remarks


    If you use not only IDEs, but also FindBugs, then methods that potentially return nullshould be marked not only as @Nullable, but also as @CheckForNull- FindBugs will "calm down" only if it "sees" @CheckForNull.

    Nice bonuses nishtyaki side effects


    This is a lyrical digression from the topic, but it talks about the immediate benefits that the introduction of analysis brought in Project X. More precisely, it helped identify the pitfalls of unit tests.

    Once upon a time there were tests that did not fall, but were not very cleverly written. Yes, you were not mistaken, this is JUnit 3:

    public void test() throws Exception {
            final Calendar calendar = myVeryCleverNonStandardApiCall();
            final int year = calendar.get(Calendar.YEAR);
            assertEquals(1997, year);
            assertEquals("1.1", System.getProperty("java.specification.version"));
    }
    

    Only a few years passed, and it was noticed that the tests broke. That is, it still compiles, but it no longer works. Tests were "fixed", so much so that the mosquito of the nose does not tarnish. In short, no one noticed anything:

    public void test() throws Exception {
            final Calendar calendar = myVeryCleverNonStandardApiCall();
            final int year = calendar.get(Calendar.YEAR);
    //      assertEquals(1997, year);
    //      assertEquals("1.1", System.getProperty("java.specification.version"));
    }
    

    After a few years, it even stopped compiling, and good statistics were still needed. The tests were repaired again. Since it was impossible to get rid of NPE, and throwing a large chunk of code would attract attention, the old test was disguised, and a new one was introduced with a second delay for averting eyes:

    public void test() throws Exception {
            Thread.sleep(1000);
    }
    public void _test() throws Exception {
            final Calendar calendar = null; //myVeryCleverNonStandardApiCall();
            final int year = calendar.get(Calendar.YEAR);
    //      assertEquals(1997, year);
    //      assertEquals("1.1", System.getProperty("java.specification.version"));
    }
    

    Conclusion?

    Sometimes it makes sense to raise the level of Null pointer access and Potential null pointer access to “Error” even for legacy code. Especially for legacy code. You can’t even imagine what “bison” is capable of, who created your product literally from scratch twenty years ago and safely left for some Google or Amazon ten years ago.

    Wishes


    If there are people among readers using C #, please comment - is the problem relevant for C # in the light of the presence of such a pleasant thing as Nullable , and if so, by what means is it solved?

    Also popular now: