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.