IntelliJ IDEA @ParametersAreNonnullByDefault for a

2019-01-27 00:52发布

问题:

I use IntelliJ's null-checking mechanism to prevent nullpointer crashes. I've successfully set up all Java method parameters to be @NonNull by default using this answer.

After creating package-info.java which is used to define package annotations in Java. All java files which are direct descendants of that package have methods with default @NonNull parameters in my Android Studio project.

The obvious problem is that I need to define @ParametersAreNonnullByDefault for all Java classes in that package i.e. including all subpackages.

How can I define @ParametersAreNonnullByDefault so that it propagates down to all subpackage java files? I want to have all of my internal code's method annotated with @NonNull by default.

/**
  * File: package-info.java
  * Make all method parameters @NonNull by default
  */
  @ParametersAreNonnullByDefault
  package com.intive.nearbyplaces.main;

  import javax.annotation.ParametersAreNonnullByDefault;

回答1:

The problem was that I couldn't force Android Studio to respect @ParametersAreNonnullByDefault in all subpackages (i.e. recursively for all java files in com.company.name)

I wrote a gradle script, however, to check whether package-info.java files are generated inside each of the subpackage folders and create it if necessary. The script runs just before assembleDebug task.

So it turns out it is possible to force @ParametersAreNonnullByDefault annotation on all java classes in your project.

Download the source here. Remember to replace line 19 with your package name.

Usage:

apply plugin: 'com.android.application'
apply from: 'nonnull.gradle'

android {
   compileSdkVersion 24
   buildToolsVersion "24.0.2"

   [...]
}

Remember to include package-info.java files in .gitignore

//File: .gitignore 

package-info.java

Checkstyle rule:

<module name="JavadocPackage"/>

checks whether package-info.java is included in each subpackage.

Source:

/**
 *  File: nonnull.gradle
 *
 *  Generates package-info.java for appropriate packages
 *  inside src/main/java folder.
 *
 *  This is a workaround to define @ParametersAreNonnullByDefault for all Java classes in a package
 *  i.e. including all subpackages (note: edit package name in line no. 19).
 */
task generateNonNullJavaFiles(dependsOn: "assembleDebug", type: Copy) {
    group = "Copying"
    description = "Generate package-info.java classes"

    def infoFileContentHeader = getFileContentHeader();
    def infoFileContentFooter = getFileContentFooter();

    def sourceDir = file( "${projectDir}" + File.separatorChar + "src" + File.separatorChar +
            "main" + File.separatorChar + "java" + File.separatorChar +
            "com" + File.separatorChar + "company" + File.separatorChar + "name" )
    sourceDir.eachDirRecurse { dir ->
        def infoFilePath = dir.getAbsolutePath() + File.separatorChar + "package-info.java"

        if (!file(infoFilePath).exists()) {
            def infoFileContentPackage = getFileContentPackage(dir.getAbsolutePath());
            new File(infoFilePath).write(infoFileContentHeader +
                    infoFileContentPackage + infoFileContentFooter)
            println "[dir] " + infoFilePath + "  created";
        }
    }
    println "[SUCCESS] NonNull generator: package-info.java files checked"
}

def getFileContentPackage(path) {
    def mainSrcPhrase = "src" + File.separatorChar + "main" + File.separatorChar +
            "java" + File.separatorChar
    def mainSrcPhraseIndex = path.indexOf(mainSrcPhrase)
    def output = path.substring(mainSrcPhraseIndex)

    // Win hotfix
    if (System.properties['os.name'].toLowerCase().contains('windows')) {
        output = output.replace("\\", "/")
        mainSrcPhrase = mainSrcPhrase.replace("\\", "/")
    }

    return "package " + output.replaceAll(mainSrcPhrase, "").replaceAll(
            "/", ".") + ";\n"
}

def getFileContentHeader() {
    return  "/** javadoc goes here \n */\n" +
            "@ParametersAreNonnullByDefault\n" +
            "@ReturnValuesAreNonnullByDefault\n"
}

def getFileContentFooter() {
    return  "\n" +
            "import javax.annotation.ParametersAreNonnullByDefault;\n" +
            "\n" +
            "import edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault;"
}

Extended version which generates package info files for src main, test & androidTest folders can be found here.

Findbugs & Lint:

It does work with findbugs & lint.