3

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

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 的检查员我的代码是干净的?

这是一个MCVE ,可在 github 上使用 Android Studio 3.4 beta 2 构建:

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)通话中也会出现同样的问题。具有讽刺意味的是,.map(Bar::getFoo)尽管肯定会抛出一个NullPointerException.

4

1 回答 1

2

这是一个 Android Studios 错误,因为这里没有 Android Studios 建议。

无警告:

无警告

应用建议并收到警告:

警告

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

插入步骤

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

这是针对此问题的真正M CVE:

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);
    }
}
于 2019-01-28T12:44:26.737 回答