Android Studioを使い、Kotlin言語の特徴を解説する本連載「Android Studioで始めるKotlin入門」。連載第1回では、Kotlinの大まかな構文を説明しました。今回からは、Kotlin言語の個々の機能について解説します。
 なお本連載では、他言語(特にJava)からKotlinに移行するに当たって重要と思われる機能を中心に解説します。本連載で解説されなかった機能については適宜、下記の公式レファレンスを参照してください。
Reference - Kotlin Programming Language
2種類の変数定義〜varとval〜
 第1回でも少し解説しましたが、Kotlinには2種類の変数定義用のキーワードがあります。varを使うと、他のプログラミング言語と同様、一般的な「変更可能」な変数が定義できます。一方、valを使うと、「変更不可」の変数となります。
 valで定義する変数は、厳密には「再代入不可」ということです。そのため、変数の持つプロパティや配列の要素の書き換えなどは問題なく行えます。リスト1にvarとvalの使用例を示します。
var i1: Int = 1 //通常の変数
val i2: Int = 1 //再代入不可変数
i1 = 2  //問題無く書き換え可能
i2 = 3  //再代入不可。コンパイル時に「val cannot be reassigned」エラー発生

//以下は連載第1回で示したイベントハンドラコード
val textView = findViewById<TextView>(R.id.myLabel) as TextView
……
button.setOnClickListener { v ->
   //valで定義したtextViewのtextプロパティは書き換え可能
   textView.text = "Clicked !"
}

//arrayOfは引数を並べた配列を作る関数
val ar1: Array<String> = arrayOf("abc", "def", "ghi")
//配列の要素も書き換え可能
ar1[2] = "jkl"
リスト1 varとvalの使用例
 valで定義する変数はJavaでいうと「final」を付けて定義した変数に相当するものです。普段自分で書くJavaコードで、変数にfinalを使うのは定数を宣言するときぐらいで、さほど多くないかもしれません。しかし、Kotlinではvalという明確に異なるキーワードが用意されています。
 「再代入不可の変数」というと特殊な機能に思えますが、例えばHaskellなどの純粋な関数型言語は「全ての変数が再代入不可である」という特徴を持っています。Scalaなどの関数型言語の影響を受けた言語では、通常の再代入可能な変数と、関数型言語由来の「再代入不可」な変数の両方が扱えるようになっており、Kotlinも同様です。

 Kotlinを使い始めたばかりですと「varとvalをどう使い分けるか」について悩むかもしれませんが、例えば「書き換えが必要と分かっている変数以外は、まずはvalで書いてみて、必要に応じてvarに変える」ぐらいの感覚で慣れていくのもいいかもしれません。
 「再代入不可」な変数がどのようなメリットを生み出すかについては、興味が出たところでぜひ調べてみてください。関数型言語の奥深さの一部に触れられること間違いなしです。
 なお今後解説しますが、Kotlinではクラスにプロパティを持つことができ、プロパティが「読み書き両方可能か」「読み取り専用か」を定義する際にも、それぞれvarとvalを使って区別する仕様となっています。
パッケージ定義とimport
 Javaのpackage文およびimport文は、そのまま使用可能です(リスト2)。
package jp.co.atmarkit.kotlinsample

import java.util.*
リスト2 package、importの使用例
 なお、Kotlinは自動的に表1のパッケージをインポートしています。
表1 Kotlinで自動的にインポートされるパッケージ
パッケージ名
概要
kotlin.*
Kotlinのコア機能。基本データ型などを含む
kotlin.annotation.*
アノテーション関連機能
kotlin.collections.*
List、Set、Mapなどのコレクション関連機能
kotlin.comparisons.*
Comparator(比較関数)生成関連機能
kotlin.io.*
ファイルおよびストリーム関連機能。println関数もここで定義されている
kotlin.ranges.*
範囲(Range)機能(後述)
kotlin.sequences.*
遅延処理するコレクション機能
kotlin.text.*
文字列処理および正規表現関連機能
java.lang.*
Javaのコア機能(Java VM用にコンパイルした場合)
kotlin.jvm.*
Javaプラットフォーム用機能(Java VM用にコンパイルした場合)

 Javaで書く場合と同様、Javaの任意のパッケージをimportできるので、使い慣れたライブラリをそのままKotlinでも使用可能です。
基本データ型
 int、shortといったクラスではないプリミティブ型を持つJavaに対し、Kotlinはプリミティブ型を持っておらず、表2のような定義となっています。Int、Longなど、Javaのプリミティブ型の先頭を大文字にした名前の型が含まれていますが、これらはJava VM用にコンパイルされた場合、基本的にプリミティブ型として処理されます。
表2 Kotlinの基本データ型
型名
概要
Double
64bit浮動小数点
Float
32bit浮動小数点
Long
64bit整数
Int
32bit整数
Short
16bit整数
Byte
8bit整数
Char
文字型
Boolean
真偽型
Array
配列型
String
文字列型

 なお、Kotlinにおいては配列は「Array」クラスで定義されており、「Array<型名>」のように、ジェネリクスを使って型を定義する点が、Javaとは異なっているので注意してください(リスト3)。
val ar1: Array<String> = arrayOf("abc", "def", "ghi")

println(ar1[0])  // abcが出力される
ar1[2] = "jkl"   // インデックスを指定して配列の中身を書き換える
println(ar1[2])  // jklが出力される

println(ar1.size) //3が出力される。配列数をsizeプロパティで取得可能
リスト3 配列の定義
 配列の内容の取得および書き換えは、Javaと同様に[]でインデックスを指定して行います。Javaでは配列の長さをlength構文で取得できましたが、Kotlinにおいては配列の長さはsizeプロパティで取得します。
 なおKotlinでは、Javaのプリミティブ型に相当するInt、Longなどの数値データ型の配列については、別途IntArray、LongArrayなどの専用の配列クラスが存在します。これらはJava VM用にコンパイルされた場合、純粋なプリミティブ型の配列として処理されます。いわゆるボクシング、アンボクシング(プリミティブ型とラッパークラスの自動変換)は発生しません。
//intArrayOfはIntArray型の配列を作る関数
var iar: IntArray = intArrayOf(1,2,3,4,5)
println(iar[4]) // 5

iar[4] = 10
println(iar[4]) // 10
リスト4 プリミティブ型配列の定義
関数(≠メソッド)
 何らかの処理を書く場合、「全てクラス内のメソッドとして記述しなければならない」JavaやC#とは異なり、Kotlinは特定の処理の塊をクラスなしで関数として定義できます。前回から使用しているprintln関数、前述の配列を作るarrayOf、intArrayOf関数などが一例です。
 関数はパッケージのトップレベルに、クラスと同列に定義できるため、import文でパッケージをインポートすると、そのパッケージで定義された関数も呼び出せるようになります。
 import文で関数を呼び出せる辺りは、強いて言えば、Java SE 5.0で導入された「staticインポート」やC# 6.0の「using static」に少し似ているかもしれませんが、クラスなしで関数が存在する点などは、まずクラスありきのJavaやC#とは大きく異なる部分ですので注意してください。
Java SE 5.0の新機能、staticインポートを習得する
C# 6.0で知っておくべき12の新機能 〜 using static
データ型の省略
 Kotlinには「型推論」と呼ばれる機能があり、変数を定義する際にデータ型が推測可能な場合、データ型指定を省略できます。
//普通の定義
val message: String = "FizzBuzz start!"
val a1: Int = 1;
var iar1: IntArray = intArrayOf(1,2,3,4,5)

//型推論を使った定義
val message2 = "FizzBuzz start!"
val a2 = 1;
var iar2 = intArrayOf(1,2,3,4,5)
リスト5 型推論のサンプル
 C#プログラマーであれば、C# 3.0以降でサポートされたvarキーワードと同様の機能といえば、その利便性の高さを理解できることでしょう。Javaにおいては、Java 7で「ジェネリック型」については型推論がサポートされたものの、通常の変数定義においては依然として「型名 変数名 = new 型名();」のように冗長な記述を強制され続けています。
 そんなJavaプログラマーにとっての朗報です。Kotlinを使えば「型推論を使えない言語なんて……」と他言語プログラマーに揶揄(やゆ)されることもなくなりますね。

制御文
 制御文については、Javaと似通っていますが、Kotlinらしい機能もあるので、一通り見ておきましょう。
if式
 KotlinのifはJavaとほぼ同じように使えますが、Javaのifが「文」であるのに対し、Kotlinのifは「式」である、つまり「値を返せる」という違いがあります。リスト6に幾つかのパターンを示します。
val a = 10
//シンプルなif
if(a >= 10)
 println("Big")

//else付きのif
if(a >= 10)
 println("Big")
else
 println("Small")

//{}を使ってブロックにする
if(a >= 10){
 println("Big")
}else{
 println("Small")
}

//ifは式なので値を返せる。ここではif式の結果("Big"または"Small")を変数に代入している
val ret = if(a >= 10) "Big" else "Small"

//ifを式として使う場合、elseは必須
val ret2 = if(a >= 10) "Big" //エラー発生
リスト6 Kotlinのif
 後半に示した、ifを式として使うパターンは注目です。なお、ifを式として使ってその結果を使用する場合はelseが必須です。これは、elseがないと、返す値がなくなってしまうためです。最後のケースのようにelseを省略するとコンパイル時にエラーが発生します。
when
 whenはJavaの「switch〜case」文に似た機能ですが、Kotlinではより強力な機能として生まれ変わっています。
 まずは基本的な数値を比較するパターンから見てみましょう(リスト7)。
val value = 100
when(value){
 100 -> println("100!")
 200 -> println("200!!")
 300 -> println("300!!!")
 else -> println("ELSE")
}
リスト7 基本的なwhenの使用方法
100!
処理結果
 Javaのswitchとは記法が違いますが、パッと見ただけで処理内容は理解できることでしょう。ケースごとに「case 値: 処理内容」の代わりに「値 -> 処理内容」となっています。ケースの終了を表すbreakはなく、いずれのケースにも該当しなかった場合の処理は、defaultの代わりにelseを使って記述します。

 Kotlinでは、文字列を対象とした分岐もリスト8のように書けます。Javaのswitchは長らく文字列を扱えず、Java 7から文字列に対応しました(C#では以前から対応していましたね)。
val str = "aaa"
when(str){
 "aaa" -> println("aaa!")
 "bbb" -> println("bbb!!")
 else -> println("ELSE")
}
リスト8 文字列を対象としたwhenの使用方法
aaa!
処理結果
whenにおける条件の複数指定や範囲指定
 さて、Kotlinのwhenの機能はまだまだこんなものではありません。リスト9は、条件を「,」(カンマ)で複数指定したり、「範囲」(Range)で指定したりする例です。なお、「範囲」については次回以降で扱う予定です。
val value2 = 13
when(value2){
 in 1..10 -> println("1..10")   //1以上10以下。inと..を使って範囲指定
 11, 12 -> println("11 or 12")  //11か12。カンマで複数指定
 in 13..20 -> println("13..20") //13以上20以下
 !in 1..20 -> println("ELSE")   //「1以上20以下」以外。!を使って条件反転
}
リスト9 whenにおける条件の複数指定や範囲指定
13..20
処理結果
引数なしのwhen
 さらに、whenは引数を取らないリスト10のような書き方もできます。この場合、whenは並べられた条件式を上から評価し、最初に合致した条件のみを処理します。複数の条件を列挙して処理する場合、Javaなどではif〜elseを連ねて書くため、インデントがどんどん増えて読みづらい記述になってしまいますが、Kotlinのwhenであればすっきりと書けます。ここまで来ると「そもそもifすら要らないのでは?」とまで思えてきますね。
val value3 = 13
val value4 = 5
when{ //引数無しのwhen
 value3 < 20 -> println("value3 < 20") //任意の条件式を書ける
 isEven(value3) -> println("Even")     //条件には関数なども書ける
 value4 > 0 -> println("value4 > 0")
 else -> println("ELSE")
}
リスト10 引数なしのwhen
value3 < 20
処理結果(最初に合致しただけ条件だけが処理される)
whenを「式」として使う
 また、Kotlinのwhenはifと同様に「式」としても扱えます(リスト11)。ifの場合と同様、elseを省略するとコンパイル時にエラーが発生します。
val value5 = "Doi"
val value6 = when(value5){ //when式の結果を変数に代入
 "Doi" -> "土井"
 "Yamada" -> "山田"
 else -> "それ以外の人" //elseがないとコンパイル時にエラー
}
println(value6)
リスト11 whenを「式」として使う
土井
処理結果
 このように、Kotlinのwhenは、一見Javaのswitch文の名前が変わっただけと思いきや、ifの存在価値すら揺るがしかねないほど便利な機能を備えています。ぜひ積極的に使っていきましょう。
for
 リスト12のように、KotlinのforはJavaの「拡張for文」と似た書き方になっています。Javaの拡張for文やC#の「foreach文」のように、配列やコレクションから1つずつ要素を取り出して処理します。ただし、JavaにおいてC言語由来のお約束になっていた「for(初期化 ; 条件 ; 増分処理)」という書き方ができなくなっている点には注意が必要です。こういったケースでは「範囲」を使って処理しましょう。
for(i in arrayOf(1,2,3)){ //配列から1つずつ取り出す
 println(i)  //1〜3を出力
}

//古くからの書き方はもうできない
//for(i=0;i<10;i++)
//  println(i)

//上記と同じ処理
for(i in 0..9){ 範囲から1つずつ取り出す
 print(i) //0〜9を出力
}

//あるいは範囲の別記法を使ったパターン
for(i in 0 until 10){
 println(i) //0〜9を出力
}
リスト12 forの使用方法
 配列から要素だけではなくインデックスも併せて取得したい場合はリスト13のような書き方も可能です。
val names = arrayOf("Doi", "Yamada", "Sakurai")

//配列からインデックスと要素の両方を取得する
for((index, elem) in names.withIndex()){
 println("$index: $elem") //文字列に変数を埋め込んで出力(詳細は次ページで解説)
}
リスト13 インデックスと要素の両方を取得する例
0: Doi
1: Yamada
2: Sakurai
処理結果
 ここでは、配列の「withIndex」メソッドを使い、インデックスと要素の両方を取得してループ内で使用しています。なお、メソッドが複数の値を返していることに驚くかもしれませんが、これはfor文の特別な機能ではなく、Kotlinのメソッド・関数全般で使うことのできる大変便利な機能です。次回以降解説予定ですのでお楽しみに。
whileループ、do〜whileループ
 whileループ、do〜whileループについてはJavaとほぼ同じ構文で扱えるため、解説は省略します。

文字列周りの便利機能
 最後に、「文字列テンプレート」「複数行文字列リテラル」、そして「trimMargin」メソッドなど文字列周りの便利機能を紹介します。いずれも小粒ですが使うと便利さが伝わってくる機能です。
文字列テンプレート
 アプリケーションのメッセージなどで、文字列の中に変数の値を埋め込むコードを書く場面はよくあることでしょう。JavaであればString.formatメソッドなどを使ったり、あるいはチマチマと+演算子を使って文字列をひたすら結合したりしていく必要があります。
 Kotlinには文字列テンプレートと呼ばれる機能があり、リスト14のように文字列リテラルに変数を直接埋め込めます。「$変数名」で変数の値をそのまま埋め込めますが、変数名をスペースなどで明示的に区切れない場合は「${変数名}」のように{}を使って変数名を指定します。また、{}の中でプロパティや配列の値などを参照して文字列に埋め込むこともできます。
val ar1: Array<String> = arrayOf("abc", "def", "ghi")
val name = "田中"
val age = 32;
println("Name: $name, Age: $age") //「$変数名」で文字列に埋め込める

//どこまでが変数名か分からないケースは{}で囲む
println("${name}さんの年齢は${age}歳です")
//変数だけでなく、プロパティや配列の値も表示可能。その場合は必ず{}で囲む
println("配列の長さ: ${ar1.size}, 配列の値: ${ar1[2]}")
リスト14 文字列テンプレートのサンプル
 C#プログラマーであれば、C# 6.0で導入された文字列補間とほぼ同じ機能と考えれば理解しやすいですね。アプリ開発などで多用するであろう、Kotlinの大変有用な機能です。
複数行文字列リテラル
 文字列リテラルが複数行にわたる場合、Javaを含む幾つかの言語では、リスト15のように、改行文字を含めて文字列を連結するようなコードを書く必要があります。
String sql = "SELECT * \n" +
 "FROM MyTable \n" +
 "WHERE cond > 1\n";
リスト15 Javaでの複数行文字列リテラル
 こうしたコードは書くのが面倒なため、幾つかの言語では「ヒアドキュメント」と呼ばれる「特定の文字列を使って文字列リテラルを開始し、次に同じ文字列が出てくるまで、複数行にわたる文字列リテラルを作る」機能が搭載されています。Kotlinではリスト16のように「"""」(ダブルクオート3個)で文字列リテラルを開始し、次に「"""」が出てくるところまでまとめて、複数行文字列リテラルとして扱えます。
val sql = """
SELECT *
FROM MyTable
WHERE cond > 1
"""
println(sql)
リスト16 文字列テンプレートのサンプル
SELECT *
FROM MyTable
WHERE cond > 1
実行結果
文字列の各行にある最初の空白文字列を「|」まで削除する「trimMargin」メソッド
 なお、コーディングルールに従ってインデントする場合、複数行リテラルの各行の先頭に無駄なスペースが付くのが気になるかもしれません。Kotlinの文字列クラスにはデフォルトで、「trimMargin」という、文字列の各行にある最初の空白文字列を「|」まで削除するメソッドがあります。これは言葉で説明するよりもリスト17、18を見た方が早いでしょう。
val sql2 = """
   |SELECT *
   |FROM MyTable
   |WHERE cond > 1
   """.trimMargin()
リスト17 trimMarginメソッドを使ったサンプル1(文字列リテラルもインデントする例)
SELECT *
FROM MyTable
WHERE cond > 1
実行結果(「|」までスペースが削除される)
val sql3 = """
   #SELECT *
   #  FROM MyTable
   #    WHERE cond > 1
   """.trimMargin("#")
リスト18 trimMarginメソッドを使ったサンプル2(「|」の代わりに「#」を使った例)
SELECT *
 FROM MyTable
   WHERE cond > 1
実行結果(リスト18の3、4行目にある「#」の後ろのスペースは削除されていない)
 このように、複数行文字列リテラルはコードの書きやすさ、見やすさに確実に貢献してくれる、小粒ながら有用な機能です。ぜひ活用しましょう。
次回は、nullとの戦いに終止符を打つ
 今回はデータ型や変数定義、制御構文などの基本的な機能について解説しました。次回はKotlinの「null安全」の仕組みについて解説します。多くのプログラマーが長年にわたって苦しみ続けてきたnullとの戦いに終止符を打つ(!?)とても魅力的な機能です。ご期待ください。

1行目はタイトルです(この行にタイトルとなる文字を書き込んで下さい)
2行目から本文です。