Kotlinによって生成された不要なnullチェックをカバーする方法


9

次の最小限のKotlinの例を検討してください。

fun <U> someWrapper(supplier: () -> U): () -> (U) {
    return { supplier() }
}

fun foo(taskExecutor: TaskExecutor): Int {
    val future = CompletableFuture.supplyAsync(someWrapper {
        42
    }, taskExecutor::execute)
    return future.join()
}

@Test
public void shouldFoo() {
    assertThat(foo(), is(42));
}

Jacocoにはブランチカバレッジルールがありますが、上記のコードでは失敗し、2つのブランチのうちの1つはsomeWrapper呼び出しの行でカバーされていません。残念ながら、それsomeWrapperが呼び出されるすべてのクラスを除外することは私にとってオプションではありません。

逆コンパイルされたJavaコードを見る:

public final int foo(TaskExecutor taskExecutor) {
    Object var10000 = WrappersKt.someWrapper((Function0)null.INSTANCE);
    if (var10000 != null) {
        Object var2 = var10000;
        var10000 = new Foo$sam$java_util_function_Supplier$0((Function0)var2);
    }

    Supplier var3 = (Supplier)var10000;
    Function1 var4 = (Function1)(new Function1(this.taskExecutor) {
        // $FF: synthetic method
        // $FF: bridge method
        public Object invoke(Object var1) {
        this.invoke((Runnable)var1);
        return Unit.INSTANCE;
        }

        public final void invoke(Runnable p1) {
        ((TaskExecutor)this.receiver).execute(p1);
        }

        public final KDeclarationContainer getOwner() {
        return Reflection.getOrCreateKotlinClass(TaskExecutor.class);
        }

        public final String getName() {
        return "execute";
        }

        public final String getSignature() {
        return "execute(Ljava/lang/Runnable;)V";
        }
    });
    CompletableFuture future = CompletableFuture.supplyAsync(var3, (Executor)(new Foo$sam$java_util_concurrent_Executor$0(var4)));
    var10000 = future.join();
    Intrinsics.checkExpressionValueIsNotNull(var10000, "future.join()");
    return ((Number)var10000).intValue();
}

問題はif (var10000 != null)、IDEによって不必要であるとマークされている(常にtrueである)ブランチにあると思います。

すべてのブランチをカバーできるようにコードを調整することはどういうわけか可能ですか?コンパイラがその余分なnullチェックを生成しないことを確認することによって?私は両方のコードを変更することができますfoo(..)し、someWrapper(..)限り、私は装飾が施され、ラムダを供給することができるよと。

私はKotlin 1.3.50とJacoco 0.8.4を使用しています。

編集。

明らかな回避策の1つは、supplyAsync(someWrapper { ... })一部のutilsクラスに抽出し、そのクラスのみを除外することです。

fun <U> supplyAsync(supplier: () -> U, executor: TaskExecutor): CompletableFuture<U> {
    return CompletableFuture.supplyAsync(someWrapper { supplier() }, executor::execute)
}

これで十分ですが、ブランチが必要ないKotlinによってブランチが追加された理由を知りたいと思っています。


私は取得Type inference failedあなたのサンプルコードのコンパイルをしようとするとき。そのまま使用できるサンプルコードを提供できればすばらしいでしょう。たとえば、taskExecutorcontrollerは不明です。
Enselic

@Enselicは、煩わしいエラーを取り除くために小さな編集を追加しました。これで十分な本格的なコードに拡張するつもりはありません。
BKE、

1
JaCoCoがKotlinをサポートするために徐々に適応する方法(github.com/jacoco/jacoco/releasesを参照し、「Kotlinコンパイラーによって追加された」を検索してください)を見ると、これは遅かれ早かれ修正されるもう1つのギャップだと思います。カバレッジの上限を真剣に考えている場合は、問題を報告することをお勧めします。
PiotrK

回答:


1

の戻り値がsomeWrapperのインスタンスとしてのみ使用されることを意図している場合は、戻り値の型としてSupplier明示的に使用することにより、不要なnullチェックを削除できますSupplier

fun <U> someWrapper(supplier: () -> U): Supplier<U> {
    return Supplier { supplier() }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.