やりたいこと
以下のように、デフォルト引数を設定した関数で、引数が外部から渡されたものか、内部的に設定されているデフォルト引数か判定します。
fun foo(v: Int? = ...)
やり方
以下のようにすればできます。
サンプルコードでは、外部から値を渡した場合その値が、渡さなかった場合default
が出力されます。
また、Int.MIN_VALUE
(= ABSENT_VALUE
に設定した値)を渡した場合も正常に判定が行えます。
val ABSENT_VALUE: Int? = Integer.valueOf(Int.MIN_VALUE)
fun foo(v: Int? = ABSENT_VALUE) {
if (v === ABSENT_VALUE) println("default") else println(v)
}
// -> default
foo()
// -> 1
foo(1)
// -> -2147483648
foo(Int.MIN_VALUE)
原理
Kotlin
では===
/!==
でインスタンスの比較が行えます。
このため、特定のインスタンスをデフォルト値に設定すると、値が指定されているかのチェックをすることができます。
比較部分(抜粋)
if (v === ABSENT_VALUE) println("default") else println("not default")
ここで、以下2点については注意が必要です。
-
ABSENT_VALUE
は確実に固有のインスタンスになるよう指定する- 特にプリミティブ型に対するラッパー型などはインスタンスがキャッシュされている場合が有る
- 引数がプリミティブ型(
non-null
なInt
など)の場合、この方法は使えない- 回避方法は「この値は
null
とみなす」ようなマジックナンバーを導入するしか無い
- 回避方法は「この値は
感想
こんなトリッキーなことはしないようにしましょうニッチなテクニックですが、Java
向けのライブラリに関連するコードをKotlin
化したい時なんかに使えることが有るかなと思います。
追記
Integer
型等のインスタンス比較に関する挙動は今後変更の可能性が有るようです。
少なくともJava 18
では紹介したコードで成功していましたが、今後成功しなくなる可能性が有ります。
対応方法も考えてみましたが、流石に現実的な感じにはなりませんでした。