防止 Android Studio 检查将 Java 流链标记为包含可能的 NullPointerException

Posted

技术标签:

【中文标题】防止 Android Studio 检查将 Java 流链标记为包含可能的 NullPointerException【英文标题】:Prevent Android Studio Inspection from Flagging Java Stream chain as containing possible NullPointerException 【发布时间】:2019-06-18 23:27:44 【问题描述】:

我正在编写一个实用方法来提取一组已包装的不可打包对象:

public interface UnparcelableHolder<U> 
    @Nullable U getUnparcelable();


public final class FragmentUtil 
    @Nullable
    public static <U> List<U> getUnparcelableHolderListArgument(
            @Nonnull Fragment fragment,
            @Nonnull Class<UnparcelableHolder<U>> unparcelableHolderClass,
            @Nonnull String key
    ) 
        @Nullable final Bundle arguments = fragment.getArguments();
        if (arguments == null) 
            return null;
         else 
            @Nullable final Parcelable[] parcelableArray = arguments.getParcelableArray(key);
            if (parcelableArray == null) 
                return null;
             else 
                return Arrays
                        .stream(parcelableArray)
                        .filter(unparcelableHolderClass::isInstance)
                        .map(unparcelableHolderClass::cast)
                        .filter(Objects::nonNull)
                        .map(UnparcelableHolder::getUnparcelable)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());
            
            if (unparcelableHolderClass.isInstance(parcelable)) 
                @Nonnull final UnparcelableHolder<U> unparcelableHolder =
                        Objects.requireNonNull(unparcelableHolderClass.cast(parcelable));
                return unparcelableHolder.getUnparcelable();
             else 
                return null;
            
        
    

android Studio 警告我,我的.map(UnparcelableHolder::getUnparcelable) 调用可能会导致NullPointerException。由于我之前的filter(Objects::nonNull) 电话,这不应该是可能的。如何告诉 Android Studio 的检查器我的代码是干净的?

这是使用 Android Studio 3.4 beta 2 构建的 MCVE is available on github:

build.gradle:

apply plugin: 'com.android.application'

android 
    compileSdkVersion 28
    defaultConfig 
        applicationId "com.github.hborders.streamsnonnulljsr305"
        minSdkVersion 28
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    
    buildTypes 
        release 
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        
    
    compileOptions 
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
    


dependencies 
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.google.code.findbugs:jsr305:3.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

MainActivity.java:

package com.github.hborders.streamsnonnulljsr305;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class MainActivity extends AppCompatActivity 

    class Foo 
        @Nonnull
        private final String string;

        Foo(@Nonnull String string) 
            this.string = string;
        

        @Nonnull
        String getString() 
            return string;
        
    

    class Bar 
        @Nullable
        private final Foo foo;

        Bar(@Nullable Foo foo) 
            this.foo = foo;
        

        @Nullable
        Foo getFoo() 
            return foo;
        
    

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Bar bar1 = new Bar(new Foo("foo"));
        final Bar bar2 = new Bar(null);
        final Bar[] bars = new Bar[]
                null,
                bar1,
                bar2,
        ;
        final List<String> strings = Arrays
                .stream(bars)
                .map(Bar::getFoo)
                .filter(Objects::nonNull)
                .map(Foo::getString)
                .collect(Collectors.toList());
        System.out.println("strings: " + strings);

    

.map(Foo::getString) 调用也会出现同样的问题。具有讽刺意味的是,Android Studio 并没有抱怨我的 .map(Bar::getFoo) 调用,尽管它肯定会抛出 NullPointerException

【问题讨论】:

getUnparcelable()的签名是什么? 我在上面的 sn-p 中声明了它,但我只是注意到我在转录时弄错了可空性。我编辑了帖子。这是签名:public interface UnparcelableHolder&lt;U&gt; @Nullable U getUnparcelable(); 我不确定它是抱怨 getUnparcelable()null 对象调用,还是 getUnparcelable() 可能返回 null 对象。尝试暂时将签名更改为 @NotNull U getUnparcelable(); 并查看 Lint 警告是否更改。如果是这样,那么它在抱怨来自getUnparcelable() 的返回值。如果没有,那么您可能只是遇到了 Lint 限制,因为它无法知道在您的情况下 filter() 恰好返回非null 对象。 我认为它抱怨getUnparcelable()null 调用,因为.map(unparcelableHolderClass::cast) 也可以返回null,它不会抱怨。 【参考方案1】:

这是一个 Android Studios 错误,因为 Android Studios 的建议在这里都不起作用。

没有警告:

应用建议并收到警告:

它还建议在已经存在时插入.filter(Objects::nonNull) 步骤。

所以这是一个明确的 AS 错误。

这是针对此问题的真正 MCVE:

import android.support.annotation.Nullable; // or any nullable you care to use

import java.util.Arrays;
import java.util.Objects;

public class MCVE 

    class Foo 
    

    class Bar 
        @Nullable
        private final Foo foo;

        Bar(@Nullable Foo foo) 
            this.foo = foo;
        

        @Nullable
        Foo getFoo() 
            return foo;
        
    

    public void mcve() 
        final Bar[] bars = new Bar[]
                new Bar(new Foo()),
        ;
        Arrays.stream(bars)
                .map(Bar::getFoo)
                .filter(Objects::nonNull)
                .map(Foo::toString);
    

【讨论】:

提交此错误的合适位置是什么?

以上是关于防止 Android Studio 检查将 Java 流链标记为包含可能的 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

怎么让android studio的编译的aar防止反编译

android笔记——android studio环境搭建

android studio怎么更新android-ndk

在列内检查特定字符串,android studio

如何防止Android studio格式化build.gradle

如何防止 Android Studio 在项目导航中自动折叠我的包?