问题
Is there a way in kotlin to prevent function call if all (or some) arguments are null? For example Having function:
fun test(a: Int, b: Int) { /* function body here */ }
I would like to prevent null checks in case when arguments are null
. For example, for arguments:
val a: Int? = null
val b: Int? = null
I would like to replace:
a?.let { b?.let { test(a, b) } }
with:
test(a, b)
I imagine that function definition syntax could look something like this:
fun test(@PreventNullCall a: Int, @PreventNullCall b: Int)
And that would be equivalent to:
fun test(a: Int?, b: Int?) {
if(a == null) {
return
}
if(b == null) {
return
}
// function body here
}
Is something like that (or similar) possible to reduce caller (and possibly function author) redundant code?
回答1:
If you don't want callers to have to do these checks themselves, you could perform null checks in an intermediary function, and then call into the real implementation when they passed:
fun test(a: Int?, b: Int?) {
a ?: return
b ?: return
realTest(a, b)
}
private fun realTest(a: Int, b: Int) {
// use params
}
Edit: here's an implementation of the function @Alexey Romanov has proposed below:
inline fun <T1, T2, R> ifAllNonNull(p1: T1?, p2: T2?, function: (T1, T2) -> R): R? {
p1 ?: return null
p2 ?: return null
return function(p1, p2)
}
fun test(a: Int, b: Int) {
println("$a, $b")
}
val a: Int? = 10
val b: Int? = 5
ifAllNonNull(a, b, ::test)
Of course you'd need to implement the ifAllNonNull
function for 2, 3, etc parameters if you have other functions where you need its functionality.
回答2:
You could define your own inline function for it.
inline fun <R, A> ifNotNull(a: A?, block: (A) -> R): R? =
if (a != null) {
block(a)
} else null
inline fun <R, A, B> ifNotNull(a: A?, b: B?, block: (A, B) -> R): R? =
if (a != null && b != null) {
block(a, b)
} else null
inline fun <R, A, B, C> ifNotNull(a: A?, b: B?, c: C?, block: (A, B, C) -> R): R? =
if (a != null && b != null && c != null) {
block(a, b, c)
} else null
inline fun <R, A, B, C, D> ifNotNull(a: A?, b: B?, c: C?, d: D?, block: (A, B, C, D) -> R): R? =
if (a != null && b != null && c != null && d != null) {
block(a, b, c, d)
} else null
And then you can call it like
ifNotNull(a, b, c, d) { a, b, c, d ->
...
}
回答3:
NO! is the answer to your question(as far as I know)
Your best bet(assuming the function is not exposed) is what you said.
a?.let { b?.let { test(a, b) } }
If the function is exposed, and might be called without these checks, then you need to put the checks inside your function.
fun test(a: Int?, b: Int?) {
if (listOf(a, b).filterNotNull().size < 2) return
println("Function was called")
}
回答4:
If you try assigning null to any of the two Int
variables you will find that this doesn`t work. See the compile errors in the comments.
fun test(a: Int, b: Int) { /* function body here */ }
fun main(){
test(null, 0) //Null can not be value of non-null type Int
val b : Int? = null
test(0, b) // Type mismatch. Required: Int - Found: Int?
}
The example shows that in a pure Kotlin world test(a: Int, b: Int)
cannot be called with null
or even Int?
arguments. If you put Java in the mix I doubt there is a safe solution without null
checks, because you can call test(Int, Int)
with type Integer
from the Java side, which allows null. The Java "equivalent" to Int
would be @NotNull Integer
(which is not really null-safe).
回答5:
Let for Tuples (Pair & Triple)
I think the spirit of the OP was syntax, so my answer focuses on providing "let" for tuple types:
Example use:
fun main() {
val p1: Int? = 10 // or null
val p2: Int? = 20 // or null
val p3: Int? = 30 // or null
val example1 = (p1 to p2).let(::testDouble)
val example2 = (p1 to p2).let { a, b -> a * b }
val example3 = (p1 to p2 to p3).let(::testTriple)
val example4 = (p1 to p2 to p3).let { a, b, c -> a * b * c }
}
fun testDouble(a: Int, b: Int): Int {
return a + b
}
fun testTriple(a: Int, b: Int, c: Int): Int {
return a + b + c
}
Extension Funs:
// Define let for Pair & Triple
fun <P1, P2, R> Pair<P1?, P2?>.let(f: (P1, P2) -> R): R? {
return f(first ?: return null, second ?: return null)
}
fun <P1, P2, P3, R> Triple<P1?, P2?, P3?>.let(f: (P1, P2, P3) -> R): R? {
return f(first ?: return null, second ?: return null, third ?: return null)
}
// Cute "to" syntax for Triple
infix fun <P1, P2, P3> Pair<P1?, P2?>.to(third: P3?): Triple<P1?, P2?, P3?> {
return Triple(first, second, third)
}
You can replace "to" with another word (see Triple extension as a guide), and you could extend to larger tuples (but Kotlin only provides 2 out-of-the-box & sadly I don't think it can be generic).
来源:https://stackoverflow.com/questions/50742094/kotlin-call-function-only-if-all-arguments-are-not-null