Kotlin:基本構文
どうもShZgZです。
Kotlinをちょっとちゃんとやってみようかと思い、
とりあえずOfficialのReferenceを読んでいきます。
とりあえず原文で読んでいってよく分からないところは日本語版を参考にしていこうかと思います。
基本的にはJavaを知っていて、JavaとKotlinでどう変わったかを確認していく感じなりますが悪しからず。
パッケージ定義
パッケージの定義はJava同様一番上です。セミコロンが無いことを除いては変わり無しですね。
package my.demo
import java.util.*
関数定義
関数はかなり変わったというか別物です。
形としてはこんな感じ、
fun 関数名(引数名: 引数の型, ・・・): 返り値の型{
(処理内容)
return 返り値
}
返り値の型とreturnを省略して
fun 関数名(引数名: 引数の型, ・・・) = 返り値
こんな風にも書けます。関数型の言語でよくある感じの書き方になってきてるように感じます。実際にはこんな感じです。
fun sum(a: Int, b: Int): Int {
return a + b
}fun sum(a: Int, b: Int) = a + b
因みにVoidはKotlinではUnitという型になりました。
ローカル変数定義
ローカル変数はvalで定数、varで変数を表すようになりました。
val a: Int = 1
val b = 2
val c: Int
c = 3var x: Int = 3
x += 1
コメント
コメントはJavaから変化無し
// 1行の場合はスラッシュ2つ
/*
複数行の場合は"/* */"でくくる
*/
文字テンプレートの使用
文字テンプレートとは
var a = 3
var str = "a is $a" // a is 3
str = "a + 3 is ${a + 3} // a + 3 is 5
こんな感じで、"$"(ドルマーク)をつけると
文字列の中で、"$"の後の式を評価した値で置換してくれるってやつ
JavaでもString.formatとかでできたけど断然こっちの方が使い易そう。
条件式(というかif式)の使用
if文が式になりました。どういうことかと言うと
if (a > b){
println(a)
} else {
println(b)
}
今まで通り上記みたいな書き方はできますが、
println(if (a > b) a else b)
こんな感じに、ifは(a > b)を評価した結果がtrueならaを、falseならbを返す式になりました。
Javaでいうif文と3項演算子が1つになった感覚ですかね。
実際、上の2つのソースをコンパイルしたらif文と3項演算子になってました。
Null値とNullチェックの使用
KotlinではNullを代入する型と代入しない型で型が変わります。
var a : Int // nullは入らない(正確にはnullが入るとエラーになります。)
var b : Int? // nullが入る
まぁ"?"をつけるだけです。
実際は、"?"の付いている方はJavaの型と同様で、"?"が付いている方は変数に値が代入される前にnullチェックが行われ、nullの場合はエラーとなるようになります。
この状況で
println(a * b)
などとするとbがnullの可能性があるためエラーとなりますが、
if (b != null){
のようにnullチェックを行うとその中では自動でnullでは無い型へキャストしてくれます。この辺は優秀ですね。Javaじゃいちいち書かなきゃいけなかったチェックは不要になりますね。
型チェックと自動キャストの使用
Kotlinさんは賢いことに型チェックを行った変数やプロパティは自動でキャストしてくれます。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// この括弧内ではobjはStringにキャストされます。
return obj.length
}// 型チェックの括弧の外なのでここではobjの型はAnyです。
return null
}
上で、Any型はJavaのObject型、ifの中で使われている"is"は"instanceof"です。上の類似で下のような場合も自動的に型がキャストされます。
fun getStringLength(obj: Any): Int? {
if (obj !is String) return null//この括弧内ではobjはStringにキャストされます。
return obj.length
}fun getStringLength(obj: Any): Int? {
// "&&"の右側ではobjはString型にキャストされます。
if (obj is String && obj.length > 0) {
return obj.length
}return null
}
forループ
forも書き方が変わりました。
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
println(item)
}
これは型が省略された感じですね。
Javaなら、
for (String item : items)
とかなんでちょっと短くなったかなって感じです。
val items = listOf("apple", "banana", "kiwi")
for (index in items.indices) {
println("item at $index is ${items[index]}")
}
こっちのインデックスで回してく方はかなり短くなってくれてると思います。
Javaなら、
for (int i = 0; i < items.size(); i++)
ですからね。
whileループ
whileは変わり無しですね。
val items = listOf("apple", "banana", "kiwi")
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
when式
これはJavaでいうところのcaseに近いですが、caseよりも汎用的に使えそうです。
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
objの値や型で場合分けしてくれるわけです。複数の条件にしたい場合はカンマでつなげます。(1, 2 -> "One or Two"みたいに)
range
".."を使うことにより範囲を表現することができるようになりました。
if (x in 1..5) // xが1から5の間にいる場合
for (y in 1..5) // yは1から5まで1つずつ増えていく
1..5でリストが作られるとかかと思ったんですが、1から5まででforを回してるだけでした。
for (x in 1..10 step 2) {
print(x)
}
for (x in 9 downTo 0 step 3) {
print(x)
}
こんな書き方もできるらしい。
collections
forループとwhen式のところでもちょっと触れましたが、collectionsに対して
for (item in items) {
println(item)
}
とか
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
とかができて。(whenは引数無しでも大丈夫、中が3項演算子だから条件式にさえなってればいいんですね。)
fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }
ラムダ式は括弧が波括弧に変わりましたが、大体一緒ですね。
まとめ
Javaで冗長だと思ってた部分がかなり省略されてる印象です。
省略された感じはしますが、書き方とかどうも学習コストは多少上がった?感じを受けました。元々Javaだからかもしれませんが。
地味に書き方変わってるので、Java8の書き方ができる人ならまぁそんなに苦労することもなさそうに思いますが、ちょっと前のJavaまでしかわからないっていうような人だとちょっと大変かもしれませんね。
ただKotlinの方がコードを書くの自体は楽しそうな気がします。