すべて(または一部)の引数がnullの場合に関数呼び出しを防ぐ方法はkotlinにありますか?たとえば、機能を持つ:
fun test(a: Int, b: Int) { /* function body here */ }
引数がnull
の場合のnullチェックを防ぎたいのですが。たとえば、引数の場合:
val a: Int? = null
val b: Int? = null
交換したい:
a?.let { b?.let { test(a, b) } }
と:
test(a, b)
関数定義構文は次のようになると思います。
fun test(@PreventNullCall a: Int, @PreventNullCall b: Int)
そして、それは以下と同等です:
fun test(a: Int?, b: Int?) {
if(a == null) {
return
}
if(b == null) {
return
}
// function body here
}
そのような(または同様の)呼び出し元(およびおそらく関数の作成者)の冗長なコードを削減することは可能ですか?
独自のインライン関数を定義できます。
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
そして、あなたはそれを次のように呼ぶことができます
ifNotNull(a, b, c, d) { a, b, c, d ->
...
}
呼び出し側がこれらのチェックを自分で行う必要がないようにしたい場合は、中間関数でnullチェックを実行し、渡されたときに実際の実装を呼び出すことができます。
fun test(a: Int?, b: Int?) {
a ?: return
b ?: return
realTest(a, b)
}
private fun realTest(a: Int, b: Int) {
// use params
}
編集:@Alexey Romanovが提案した関数の実装を以下に示します。
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)
もちろん、機能が必要な他の関数がある場合は、2、3などのパラメーターにifAllNonNull
関数を実装する必要があります。
番号!あなたの質問に対する答えです(私の知る限り)
あなたの最善の策(関数が公開されていないと想定)は、あなたが言ったことです。
a?.let { b?.let { test(a, b) } }
関数が公開されており、これらのチェックなしで呼び出される可能性がある場合は、関数内にチェックを配置する必要があります。
fun test(a: Int?, b: Int?) {
if (listOf(a, b).filterNotNull().size < 2) return
println("Function was called")
}
OPの精神は構文だったと思うので、私の答えはタプル型に「let」を提供することに焦点を当てています。
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
}
// 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)
}
「to」を別のWordに置き換えることができ(ガイドとしてのトリプル拡張を参照)、より大きなタプルに拡張することもできます(ただし、Kotlinはすぐに使用できる2つのみを提供しますが、残念ながら、それは一般的ではないと思います)。 。
2つのInt
変数のいずれかにnullを割り当てようとすると、これが機能しないことがわかります。コメントのコンパイルエラーを参照してください。
_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?
}
_
この例は、純粋なKotlinの世界では、test(a: Int, b: Int)
をnull
または_Int?
_引数で呼び出すことはできないことを示しています。 Javaを組み合わせて使用する場合、Java側からnull
型でtest(Int, Int)
を呼び出すことができるため、Integer
チェックなしで安全な解決策があるとは思えません。 Int
と同等のJavaは_@NotNull Integer
_です(実際にはnullセーフではありません)。