¿Cómo cubrir el cheque nulo innecesario generado por Kotlin?

9

Considere el siguiente ejemplo mínimo de 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));
}

Tengo reglas de cobertura de sucursales en Jacoco, que fallan para el código anterior, diciendo que 1 de 2 sucursales no está cubierta en la línea de la someWrapperllamada. Desafortunadamente, no es una opción para mí excluir todas las clases de las que someWrapperse llama.

Mirando el código Java descompilado:

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();
}

Creo que el problema es la if (var10000 != null)rama, que incluso está marcada por el IDE como innecesaria (siempre cierta).

¿De alguna manera es posible ajustar el código de modo que sea posible cubrir todas las ramas, por ejemplo? ¿asegurándose de que el compilador no genera esa verificación nula adicional? Puedo cambiar el código de ambos foo(..)y someWrapper(..)siempre que pueda suministrar una lambda decorada.

Yo uso Kotlin 1.3.50 y Jacoco 0.8.4.

EDITAR.

Una solución obvia es extraer supplyAsync(someWrapper { ... })a algunas clases de utilidades y excluir solo esa clase, es decir:

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

Esto sería lo suficientemente bueno para mí, aunque todavía tengo curiosidad por saber por qué Kotlin agrega la rama, donde no es necesario que esté.

BKE
fuente
Obtengo Type inference failedcuando intento compilar su código de muestra. ¡Sería genial si pudiera proporcionar un código de muestra que funciona de inmediato! Por ejemplo, taskExecutory controllerson incógnitas.
Enselic
@Enselic agregó una pequeña edición para eliminar errores de distracción. No voy a expandirlo más al código completo, ya que esto debería ser suficiente para transmitir la idea.
BKE
1
Al observar cómo JaCoCo se adapta progresivamente para admitir Kotlin (consulte github.com/jacoco/jacoco/releases y busque "agregado por el compilador de Kotlin"), creo que esta es solo otra brecha que se solucionará tarde o temprano. Si se toma en serio la posibilidad de superar su cobertura, le sugiero que informe un problema.
PiotrK

Respuestas:

1

Si el valor de retorno de someWrappersolo debe usarse como una instancia de Supplier, entonces puede eliminar la verificación nula innecesaria mediante el uso explícito Suppliercomo tipo de retorno.

fun <U> someWrapper(supplier: () -> U): Supplier<U> {
    return Supplier { supplier() }
}
Leo Aso
fuente