Come coprire inutili controlli nulli generati da Kotlin?


9

Considera il seguente esempio minimo di 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));
}

Ho delle regole di copertura delle filiali in Jacoco, che non riescono per il codice sopra, dicendo che 1 delle 2 filiali non è coperto sulla linea della someWrapperchiamata. Sfortunatamente, non è un'opzione per me escludere tutte le classi da cui someWrapperviene chiamata.

Guardando il codice Java decompilato:

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

Penso che il problema sia il if (var10000 != null)ramo, che è persino contrassegnato dall'IDE come non necessario (sempre vero).

È in qualche modo possibile regolare il codice in modo che sia possibile coprire tutti i rami, ad es. assicurandoti che il compilatore non generi quel controllo null aggiuntivo? Posso cambiare il codice di entrambi foo(..)e someWrapper(..)fintanto che sono in grado di fornire un lambda decorato.

Uso Kotlin 1.3.50 e Jacoco 0.8.4.

MODIFICARE.

Una soluzione ovvia è quella di estrarre supplyAsync(someWrapper { ... })in alcune classi utils ed escludere solo quella classe, cioè:

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

Questo sarebbe abbastanza buono per me, anche se sono ancora curioso di sapere perché il ramo viene aggiunto da Kotlin, dove nessun ramo deve essere.


Ottengo Type inference failedquando provo a compilare il tuo codice di esempio. Sarebbe bello se potessi fornire un codice di esempio che funzioni immediatamente! Ad esempio, taskExecutore controllersono sconosciuti.
Enselic,

@Enselic ha aggiunto una piccola modifica per rimuovere errori di distrazione. Non ho intenzione di espanderlo ulteriormente al codice completo in quanto questo dovrebbe essere sufficiente per far passare l'idea.
BKE

1
Guardando come JaCoCo si adatta progressivamente al supporto di Kotlin (vedi github.com/jacoco/jacoco/releases e cerca "aggiunto dal compilatore Kotlin"), penso che questo sia solo un altro divario che verrà risolto prima o poi. Se sei seriamente intenzionato a completare la copertura, ti suggerisco di segnalare un problema.
PiotrK,

Risposte:


1

Se il valore restituito di someWrapperdeve essere utilizzato solo come istanza di Supplier, è possibile rimuovere il controllo null non necessario utilizzando esplicitamente Suppliercome tipo restituito.

fun <U> someWrapper(supplier: () -> U): Supplier<U> {
    return Supplier { supplier() }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.