LoginSignup
34
22

More than 5 years have passed since last update.

Kotlinには三項演算子(条件演算子)がない

Last updated at Posted at 2017-07-22

Kotlinと三項演算子(条件演算子)

GoogleがAndroidの開発言語としてKotlinを公式にサポートしたことで、最近何かと話題のKotlinですが、Kotlinには三項演算子がありません。

val max = a > b ? a : b // NG

そのかわり、Kotlinではifが式として扱われるので、以下のように書くことができます。

val max = if (a > b) a else b

だけど、Javaには三項演算子があるのに、Kotlinにはないなんて、なんか不便じゃないですか? :thinking:
なので今回は、三項演算子っぽいものを無理矢理作ってみたいと思います :muscle:

三項演算子を定義する

Kotlinには?:という演算子はないので、Operator overloadingは使えません。

しかし、infixを使うと、独自の演算子(のように見えるもの)が定義できます。
https://kotlinlang.org/docs/reference/functions.html#infix-notation

今回は、以下のようにクラスとinfix関数を定義します。

data class TernaryOperation<out T>(val condition: Boolean, val value: T)

infix fun <T> Boolean.`?`(other: T) = TernaryOperation<T>(this, other)
infix fun <T> TernaryOperation<T>.`:`(other: T) = if (this.condition) this.value else other

?:は関数名として使えないため、バッククォートで囲っています。
ただ、`?`は使えても、`:`はなぜか使えなかったので、`:`(全角)にしました。
また、KotlinでもJavaと同様に、関数名に日本語を使うことができますが、は使えません。

上記で定義した関数を使うときはこんな感じです。

val max = (a > b) `?` a `:` b

なんかそれっぽい感じになりました :sweat_smile:

Javaの三項演算子との違い

演算子の優先度

Kotlinのinfix関数は、比較演算子よりも優先度が高いです。
https://kotlinlang.org/docs/reference/grammar.html#precedence

一方で、Javaの三項演算子は、比較演算子よりも優先度が低いです。
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html

そのため、今回作った三項演算子もどきで比較演算子などを使う場合は、条件式をカッコでくくる必要があります。

Java
int max = a > b ? a : b; // OK
Kotlin
val max = a > b `?` a `:` b // NG

val max = (a > b) `?` a `:` b // OK
val max = (a > b) `?` a + 3 `:` b - 5 // OK

式の評価

saka1029さんのコメントで気が付きました。ありがとうございます :bow:

今回作った三項演算子もどきは、値を渡しているため、条件の真偽にかかわらず両方の値が評価されてしまいます。

Java
int value = array.length > 0 ? array[0] : -1; // OK
Kotlin
val value = array.isNotEmpty() `?` array[0] `:` -1 // arrayが空のときArrayIndexOutOfBoundsException

これを回避するとなると、T型ではなく() -> T型にしないといけないですが、もはやif式との違いがなくなってしまいますね :sweat_smile:

data class TernaryOperation<out T>(val condition: Boolean, val value: () -> T)

infix fun <T> Boolean.`?`(other: () -> T) = TernaryOperation<T>(this, other)
infix fun <T> TernaryOperation<T>.`:`(other: () -> T) = if (this.condition) this.value() else other()

val value = array.isNotEmpty() `?` { array[0] } `:` { -1 } // OK

エルビス演算子を使うパターン

以下のようなinfix関数を定義します。

infix fun <T> Boolean.then(other: T) = if (this) other else null

エルビス演算子を使うと、以下のように三項演算子っぽく書けます。

val max = (a > b) then a ?: b

ただし、trueの時の値にnullを使うと、(当然ですが)常にfalseの時の値になってしまいます :cry:

val body = response.isError() then null ?: response.body // always response.body

また、先ほどと同じように、優先度の問題と、式の評価の問題があります。

まとめ

ネタで作ってみましたが、やっぱり不便ですね :sweat:
自分はPythonもよく使うので、Kotlinの三項演算子の書き方はまだ慣れないです :sob:

何か感想や意見がありましたらコメントお願いします :bow:

34
22
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
22