Kotlin のドキュメントから、 すぐに試すための情報を選んでピックアップしました。 Kotlin 1.2.41 を想定しています。
基本構文
main
Java は static void main で始まる main 関数 からプログラムが開始していました。 Kotlin でも同様に main 関数 からプログラムが開始します。 JVMコンパイル では Java でいう main 関数になります。 JS コンパイル では jsファイル読み込み時に自動実行される関数になります。
fun main(args: Array<String>) {
// ...
}
または
fun main(vararg args: String) {
// ...
}
- 関数は
funで始まります。 - ここでの
main関数 はクラス内の関数ではありません。 このように定義された関数をトップレベル関数といいます。
定数・変数
変数宣言
変数の宣言には val, var を使います。 var を使うと変更可能な変数が、 val を使うと変更不可能(イミュータブル)な変数が定義できます。 scala と同じですね。
val a = "a"
var b = 1
Kotlin には型推論がありますから、 上の a は String, b は Int として扱われます。
定数宣言
定数を宣言するには、 const val を使います。 プリミティブ型またはStringの定数が宣言可能です。
const val c: Char = 'c'
const val には使える場所が決まっており、 オブジェクトの中か、トップレベルで使えます。
基本の型
Int, Long, Float, Double, Byte, Char, String, Boolean が基本の型です。 Char は次のような計算も可能です。
val c = 'c'
val d = c + 1
Kotlin では Java と異なりすべてがオブジェクト型です。 内部的にはJavaでいうプリミティブ型を使うものもあります。
アクセス修飾子
クラス、関数、メソッド・プロパティ(後述) にはアクセス修飾子を付けることができます。 Kotlin では次のアクセス修飾子が使えます。
| 修飾子 | トップレベル | クラス内 |
|---|---|---|
| public | どこからでもアクセス可能 | クラスにアクセス可能なところではアクセス可能 |
| private | ファイル内からアクセス可能 | クラス内からアクセス可能 |
| protected | (使用不可) | クラスとサブクラスからアクセス可能 |
| internal | 同一モジュール内からアクセス可能(モジュールとは、一緒にコンパイルされるファイル群のこと) | クラスにアクセス可能な同一モジュール内の場所からアクセス可能 |
フロー制御
条件分岐
if, when が使えます。
if 式
Kotlin の if は値を返すので"文"ではなく"式"とされています。 (一方、CやVB, javascriptのifは値を返しません。)
val max = if (x < y) {
y
} else {
x
}
次のように書くこともできます。
val min = if (x < y) x else y
if が式であるため、 三項演算子 ... ? ... : ... は Kotlin にありません。
When 式
C, C++, C#, Java, PHP, javascript でいう switch, Ruby の when, VB の Select に相当するものです。 こちらも値を返します。
val e = when (value) {
0 -> "ゼロ"
1 -> "いち"
2, 3 -> "に または さん"
is Int -> "Int"
is Double -> "Double"
in 0..100 -> "0 から 100 のうちのどれか"
else -> "その他のなにか"
}
ループ
for ループ
Kotlin の for ループ は、 よくある for (i = 0; i < sup; ++i) という形では使えません。 感覚的には foreach が基本です。
for (i in 1..10) {
// ...
}
(1..10).forEach {
// ...
}
コレクション、配列は、forEach メソッド(後述) を持っているので、 あまり for を使うことはありません。(経験上)
while ループ
他の言語とだいたい同じです。
while (condition) {
// ...
}
do {
// ...
} while (condition)
## 基本の型 (補足)
### 型同士の変換
各型の変換は、それぞれのオブジェクトのメソッドを使います。
1.toLong()
2L.toInt()
65.toChar() // => 'A'
'B'.toInt() // => 66
### Array
`Array<Int>`, `Array<Double>` で配列を定義します。 これさえ覚えておけば配列は使えます。 一方 `IntArray`, `DoubleArray` というのもあり、 boxing のオーバヘッドがない配列定義ができます。
val a = arrayOf(1, 2, 3, 4) // Array
val b = intArrayOf(1, 2, 3, 4) // IntArray
## クラス
### 定義
次のようにして定義できます。
class Sample(name: String) {
init {
// initialize
}
val alternativeName = if (name.length > 3) name else "+++"
}
コンストラクタパラメータに `val`, `var` を付けると、 その値がそのままオブジェクトのプロパティになります。
class Sample(val name: String)
### 種類
Kotlin にはいくつかの特別なクラスがあります。
| 宣言 | 説明 |
|:--|:--|
| `data class` | バリューオブジェクトのクラスを簡単に作成できる。 |
| `sealed class` | クラス階層を制御します。 それよりも、 `when` 式 で `else` を書かなくてもよくなる場合があります。 |
| `enum class` | |
| `interface` | インターフェース |
| `abstract class` | 抽象クラス |
| `object` | scala にあるような、シングルトンオブジェクトです。 |
## 関数型とラムダ
Kotlin では `(Int) -> Double` のようにして関数型変数を定義できます。
### 無名関数、ラムダ
// ラムダ
val a: (Int) -> Int = { it + 1 }
// 無名関数
val b = fun (p: Int): Int { return p + 1 }
### 高階関数
関数を引数にとったり、関数を返したりする関数を高階関数といいます。
fun convertIntArray(intArray: Array, conversion: (Int) -> Int): Array {
for ((i, v) in intArray.withIndex()) { intArray[i] = conversion(v) }
return intArray
}
次のようにして呼び出せます。
convertIntArray(arrayOf(1, 2, 3, 4), { it + 1 })
この関数は、最後のパラメータが関数型になりますから、次のように呼び出すことが可能です。
convertIntArray(arrayOf(1, 2, 3, 4)) {
it + 1
}
ここまでで一応Kotlinを書き始めることはできるようになったと思います。
特に IntelliJ は丁寧にガイドしてくれるので、 きっと悩むことはないと思います。
## Kotlin を使う場合に知っておくべき機能
ここまでは多くの言語にある書き方を、 Kotlin でどう書くのかの説明が主でした。
ここでは(比較的)Kotlin特有の機能を説明します。
### Null安全
Null Pointer Exception に悩まされたことのある人も多いと思います。 Kotlin には、 Swift, PHP 7.1 のように、 Nullable型があります。
var a: String?
a = "a"
a = null
型の後ろに`?`を付けると、 null になりうる型(nullable type)になります。 `?` をつけなければ、 null は認められません。
var a: String
a = "a"
a = null // => Error!
nullable型がnullでない時にのみメソッドを呼び出したいことはよくあると思います。 次のようにすると、 null でない場合にのみメソッドが呼び出されます。
x.?method()
nullable型に関連して、次の演算子は使うことがありますので覚えておきましょう。
| 演算子 | 説明 | 例 |
|:--|:--|:--|
| `!!` | nullではなく値があるものとして扱う | `x!!.length` |
| `?:` | nullではない場合に右側の値を返す | `x ?: 1` |
| `as?` | キャストができない場合に null を返す | `x as? Int` |
### 拡張
あるクラスに、あたかも最初からプロパティ・メソッドが定義されていたかのようにプロパティ・メソッドを定義する方法です。
fun Int.skip(): Int { return this + 2 }
1.skip() // => 3
val Int.next: Int
get() { this + 1 }
1.next // => 2
### ラムダのパターン
ラムダにはいくつかの書き方があります。 `(Int) -> Int` と `Int.() -> Int` で、 `this` の扱いが変わってきます。 `(int) -> Int` は `fun (Int): Int` と同じです。 `Int.() -> Int` は `fun Int.(): Int` と同じです。
`Int.() -> Int` のように、 `Receiver.` から始まる型のラムダをレシーバ付きラムダといいます。 Kotlin で DSL を作る際にも活躍します。
高階関数のところで紹介したサンプルコードをレシーバ付きラムダを使った定義に変えてみると次のようになります。
fun convertIntArray(intArray: Array, conversion: Int.() -> Int): Array {
for ((i, v) in intArray.withIndex()) { intArray[i] = v.conversion() }
return intArray
}
次のようにして呼び出せます。
convertIntArray(arrayOf(1, 2, 3, 4), { this + 1 })
レシーバはラムダ内では `this` になりますから、 `this` のもつメソッドは `this` と書かずに呼び出すことができます。 (関連: [スコープ関数分類](http://improve-future.com/kotlin-categorize-scope-functions.html))