If you have a Java interface ```public interface T...
# getting-started
v
If you have a Java interface
Copy code
public interface Transformer<OUT, IN> {
    OUT transform(IN in);
}
And a Java method
Copy code
filter(Transformer<String, String> var1)
And Kotlin wrongly assumes the second parameter to be non-
null
. How can you call it with a lambda that returns
null
?
j
Kotlin should infer platform types for those type parameters. Do you have a reproducible example of Kotlin assuming non-null here? I tested your code and I can correctly call it with a lambda returning null
v
Gradle build script with a Copy task, there the filter method
🙏 1
Copy code
plugins {
    `java-library`
}

tasks.processResources {
    filter { it.ifBlank { null } }
}
So besides somewhere having a wrong assumption or configuration, is there a way to force it to work?
In this specific case, it comes to the rescue that there is a
Closure
variant, so I can do
Copy code
tasks.processResources {
    filter(object : Closure<String>(null, null) {
        fun doCall(line: String) = line.ifBlank { null }
    })
}
but the question is more general. In other situations there might not be an appropriate alternative.
I think Kotlin is correct here, the Gradle API is declaring that everything is non-null
v
subprojects/core-api/src/main/java/org/gradle/api/package-info.java
, not
subprojects/core-api/src/main/java/org/gradle/api/file/package-info.java
but yeah. That explains where still wrong non-nullness comes from. The JavaDoc clearly says that returning
null
is a valid option and will delete the transformed line. So it is a Gradle bug. But the question remains, can I somehow trick the Kotlin compiler so that
null
is returned without writing a Java or Groovy source file that returns
null
on behalf?
e
not as far as I know 😞
this works even if a
Closure<*>
API isn't provided:
Copy code
groovy.util.Eval.xy(this, { line: String -> line.ifBlank { null } }, "x.filter { y.invoke(it) }")
(admittedly 🤢)
v
Ah, figured out the nice version of it:
Copy code
tasks.processResources {
    filter { it.ifBlank { Supplier { null }.get() } }
}
Interestingly enough this does not work in the anonymous object version
j
For by-passing the null-check, here is a trick.
Copy code
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
inline fun <T> `null but only when you know what you're doing`(): T =
    // This won't throw because all casting to generics is unchecked!
    null as T
I think I found it from a JetBrains library so they allowed this usage but again it’s only for special case.
v
Ah, thanks, will remember for non-JVM case as pure Kotlin variant.
Hm, maybe I just didn't try to execute it. While the IDE complains about
Copy code
tasks.processResources {
    filter { it.ifBlank { null } }
}
it actually works just fine.
So I think we are back to it being a Kotlin / Kotlin IntelliJ plugin issue 🙂
e
actually that is interesting. I wonder if kotlinc doesn't have the same classpath as intellij
Gradle defines
Copy code
@javax.annotation.Nonnull
public @interface NonNullApi
and
javax.annotation.Nonnull
is one of the annotations that Kotlin is supposed to recognize, but if can't see that far…