タイプList<Any>?
の変数activities
があるとします。リストがnullでも空でもない場合は、何かをしたいのですが、それ以外の場合は別のことをしたいと思います。私は次の解決策を思いつきました:
when {
activities != null && !activities.empty -> doSomething
else -> doSomethingElse
}
Kotlinでこれを行うためのより慣用的な方法はありますか?
一部の単純なアクションでは、安全な呼び出し演算子を使用できます。アクションは空のリストでの操作も考慮していないと想定します(both nullおよび空のケースを処理するため)。
myList?.forEach { ...only iterates if not null and not empty }
他のアクションのため。拡張関数を作成できます。リストをthis
として受け取るか、パラメータとして受け取るかによって、2つのバリエーションがあります。
inline fun <E: Any, T: Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): Unit {
if (this != null && this.isNotEmpty()) {
with (this) { func() }
}
}
inline fun <E: Any, T: Collection<E>> T?.whenNotNullNorEmpty(func: (T) -> Unit): Unit {
if (this != null && this.isNotEmpty()) {
func(this)
}
}
次のように使用できます。
fun foo() {
val something: List<String>? = makeListOrNot()
something.withNotNullNorEmpty {
// do anything I want, list is `this`
}
something.whenNotNullNorEmpty { myList ->
// do anything I want, list is `myList`
}
}
逆関数を実行することもできます:
inline fun <E: Any, T: Collection<E>> T?.withNullOrEmpty(func: () -> Unit): Unit {
if (this == null || this.isEmpty()) {
func()
}
}
if
またはwhen
ステートメントをより冗長なものに置き換えるので、これらをチェーンすることは避けます。そして、あなたは私が以下で言及する代替案が提供する領域にもっと入ります、それは成功/失敗の状況のための完全な分岐です。
注:これらの拡張は、null以外の値を保持するCollections
のすべての子孫に一般化されました。そして、単なるリスト以上のもののために働きます。
代替案:
Kotlinの Result ライブラリーは、応答値に基づいて「これを実行するか、それを実行する」というケースを処理する優れた方法を提供します。 Promiseについては、同じことが Kovenant ライブラリにあります。
これらのライブラリはどちらも、単一の関数から別の結果を返す方法と、結果に基づいてコードを分岐する方法を提供します。 彼らはあなたが作用する「答え」の提供者を管理していることを必要とします。
これらは、Optional
およびMaybe
のKotlinに代わる優れた手段です。
さらに拡張機能を探る(そして多すぎる)
このセクションは、ここで出された質問のような問題にぶつかったときに、Kotlinで多くの答えを簡単に見つけて、思い通りのコーディングを作成できることを示しています。世界が気に入らなければ、世界を変えてください。これは良い答えでも悪い答えでもありませんが、追加情報です。
拡張関数が好きで、式でそれらをチェーンすることを検討したい場合は、おそらく次のように変更します...
withXyz
フレーバーはthis
を返し、whenXyz
は新しい型を返す必要があります。これにより、コレクション全体を新しいものにすることができます(元のコレクションとは関係がない場合もあります)。次のようなコードになります。
val BAD_PREFIX = "abc"
fun example(someList: List<String>?) {
someList?.filterNot { it.startsWith(BAD_PREFIX) }
?.sorted()
.withNotNullNorEmpty {
// do something with `this` list and return itself automatically
}
.whenNotNullNorEmpty { list ->
// do something to replace `list` with something new
listOf("x","y","z")
}
.whenNullOrEmpty {
// other code returning something new to replace the null or empty list
setOf("was","null","but","not","now")
}
}
注:このバージョンの完全なコードは投稿の最後にあります(1)
しかし、カスタムの「これ以外の方法で」メカニズムを使用して、まったく新しい方向に進むこともできます。
fun foo(someList: List<String>?) {
someList.whenNullOrEmpty {
// other code
}
.otherwise { list ->
// do something with `list`
}
}
制限はありません。創造的で、拡張機能の力を学び、新しいアイデアを試してください。ご覧のように、このような状況を人々がコーディングする方法にはさまざまなバリエーションがあります。 stdlibは、混乱することなく、これらのタイプのメソッドの8つのバリエーションをサポートできません。ただし、各開発グループには、コーディングスタイルに一致する拡張機能を含めることができます。
注:このバージョンの完全なコードは投稿の最後にあります(2)
サンプルコード1:これは「連鎖」バージョンの完全なコードです:
inline fun <E: Any, T: Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): T? {
if (this != null && this.isNotEmpty()) {
with (this) { func() }
}
return this
}
inline fun <E: Any, T: Collection<E>, R: Any> T?.whenNotNullNorEmpty(func: (T) -> R?): R? {
if (this != null && this.isNotEmpty()) {
return func(this)
}
return null
}
inline fun <E: Any, T: Collection<E>> T?.withNullOrEmpty(func: () -> Unit): T? {
if (this == null || this.isEmpty()) {
func()
}
return this
}
inline fun <E: Any, T: Collection<E>, R: Any> T?.whenNullOrEmpty(func: () -> R?): R? {
if (this == null || this.isEmpty()) {
return func()
}
return null
}
サンプルコード2:これは、「this other else that」ライブラリの完全なコードです(単体テスト):
inline fun <E : Any, T : Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): Otherwise {
return if (this != null && this.isNotEmpty()) {
with (this) { func() }
OtherwiseIgnore
} else {
OtherwiseInvoke
}
}
inline fun <E : Any, T : Collection<E>> T?.whenNotNullNorEmpty(func: (T) -> Unit): Otherwise {
return if (this != null && this.isNotEmpty()) {
func(this)
OtherwiseIgnore
} else {
OtherwiseInvoke
}
}
inline fun <E : Any, T : Collection<E>> T?.withNullOrEmpty(func: () -> Unit): OtherwiseWithValue<T> {
return if (this == null || this.isEmpty()) {
func()
OtherwiseWithValueIgnore<T>()
} else {
OtherwiseWithValueInvoke(this)
}
}
inline fun <E : Any, T : Collection<E>> T?.whenNullOrEmpty(func: () -> Unit): OtherwiseWhenValue<T> {
return if (this == null || this.isEmpty()) {
func()
OtherwiseWhenValueIgnore<T>()
} else {
OtherwiseWhenValueInvoke(this)
}
}
interface Otherwise {
fun otherwise(func: () -> Unit): Unit
}
object OtherwiseInvoke : Otherwise {
override fun otherwise(func: () -> Unit): Unit {
func()
}
}
object OtherwiseIgnore : Otherwise {
override fun otherwise(func: () -> Unit): Unit {
}
}
interface OtherwiseWithValue<T> {
fun otherwise(func: T.() -> Unit): Unit
}
class OtherwiseWithValueInvoke<T>(val value: T) : OtherwiseWithValue<T> {
override fun otherwise(func: T.() -> Unit): Unit {
with (value) { func() }
}
}
class OtherwiseWithValueIgnore<T> : OtherwiseWithValue<T> {
override fun otherwise(func: T.() -> Unit): Unit {
}
}
interface OtherwiseWhenValue<T> {
fun otherwise(func: (T) -> Unit): Unit
}
class OtherwiseWhenValueInvoke<T>(val value: T) : OtherwiseWhenValue<T> {
override fun otherwise(func: (T) -> Unit): Unit {
func(value)
}
}
class OtherwiseWhenValueIgnore<T> : OtherwiseWhenValue<T> {
override fun otherwise(func: (T) -> Unit): Unit {
}
}
class TestBrancher {
@Test fun testOne() {
// when NOT null or empty
emptyList<String>().whenNotNullNorEmpty { list ->
fail("should not branch here")
}.otherwise {
// sucess
}
nullList<String>().whenNotNullNorEmpty { list ->
fail("should not branch here")
}.otherwise {
// sucess
}
listOf("a", "b").whenNotNullNorEmpty { list ->
assertEquals(listOf("a", "b"), list)
}.otherwise {
fail("should not branch here")
}
// when YES null or empty
emptyList<String>().whenNullOrEmpty {
// sucess
}.otherwise { list ->
fail("should not branch here")
}
nullList<String>().whenNullOrEmpty {
// success
}.otherwise {
fail("should not branch here")
}
listOf("a", "b").whenNullOrEmpty {
fail("should not branch here")
}.otherwise { list ->
assertEquals(listOf("a", "b"), list)
}
// with NOT null or empty
emptyList<String>().withNotNullNorEmpty {
fail("should not branch here")
}.otherwise {
// sucess
}
nullList<String>().withNotNullNorEmpty {
fail("should not branch here")
}.otherwise {
// sucess
}
listOf("a", "b").withNotNullNorEmpty {
assertEquals(listOf("a", "b"), this)
}.otherwise {
fail("should not branch here")
}
// with YES null or empty
emptyList<String>().withNullOrEmpty {
// sucess
}.otherwise {
fail("should not branch here")
}
nullList<String>().withNullOrEmpty {
// success
}.otherwise {
fail("should not branch here")
}
listOf("a", "b").withNullOrEmpty {
fail("should not branch here")
}.otherwise {
assertEquals(listOf("a", "b"), this)
}
}
fun <T : Any> nullList(): List<T>? = null
}
UPDATE:
kotlin 1.3はisNullOrEmpty
を提供します!
https://Twitter.com/kotlin/status/1050426794682306562
これを試して!非常に明確な。
var array: List<String>? = null
if (array.orEmpty().isEmpty()) {
// empty
} else {
// not empty
}
他の回答に加えて、セーフコール演算子を拡張メソッドisNotEmpty()
と組み合わせて使用することもできます。安全な呼び出しのため、戻り値は実際にはBoolean?
は、true
、false
、またはnull
のいずれかです。 if
またはwhen
句で式を使用するには、true
であるかどうかを明示的に確認する必要があります。
when {
activities?.isNotEmpty() == true -> doSomething
else -> doSomethingElse
}
Elvis演算子を使用した代替構文:
when {
activities?.isNotEmpty() ?: false -> doSomething
else -> doSomethingElse
}
より簡単な方法は、
if(activities?.isNotEmpty() == true) doSomething() else doSomethingElse()
必要に応じて、?.forEach
の使用を検討してください
activities?.forEach {
doSmth(it)
}
あなたが説明した振る舞いを正確に望むなら、あなたのバリアントは私が考えることができる他のより簡潔なものよりもよく読めると思います。 (ただし、単純なif
で十分です)
Kotlin 1.3で使用する実際の方法は、この回答で述べたようにisNullOrEmpty
です https://stackoverflow.com/a/48056456/2735286
以下はその使用例です。
fun main(args: Array<String>) {
var array: MutableList<String>? = null
println(array.isNullOrEmpty()) // true
array = mutableListOf()
println(array.isNullOrEmpty()) // true
array = mutableListOf("a")
println(array.isNullOrEmpty()) // false
}
この例は次のように出力します。
true
true
false
私の場合、価格はオプションです。次のようにorEmpty()
を使用してケースを処理します。これは、指定された配列またはnullの場合は空の配列を返します。
val safeArray = poi.prices.orEmpty()
if (!safeArray.isEmpty()) {
...
}
Kotlin 1.3の拡張子はisNullOrEmptyです。簡単な答えは:
if (activities.isNullOrEmpty) doSomething
else doSomethingElse
拡張は次のように定義されます。
fun <T> Collection<T>?.isNullOrEmpty(): Boolean
StringおよびArrayにも同様の拡張が存在します。