Kotlinについて

 KotlinはAndroidアプリ開発に非常に適した言語です。これを学ぶモチベーションは実際にAndroinアプリ開発を行いたいということです。 もちろんJavaでも開発はできますが、なぜKotlinをあえて用いるのでしょうか。それはKotlinが「実用主義、簡潔、安全」を目指した言語だからです。 Javaよりもシンプルにコードを書くことができます。その上に、Javaとの相互互換性があるため、JavaのプログラムをKotlinで簡単に利用することもできるのです。 Javaについての説明は他のページで行っているので、是非そちらを見てください。ここではKotlinの主な特徴を見ていきたいと思います。 KotlinはJavaと同じくJVMで動作します。そのため、JavaとKotlinのソースファイルを自由に行き来することができます。また、KotlinはJavaと同じく静的型付けで、オブジェクト指向のプログラミング言語となります。 Kotlinでは先進的な関数プログラミングの機能も取り入れらています。安全性についてもJavaよりも担保されています。例えば、静的型付け言語として型の安全性が保証されています。他にも NullPointerExceptionをなくすためNullチェックが厳格になっています。このように、JavaとKotlinは兄弟関係のようなプログラミング言語ですが、KotlinはJavaをより簡潔にまとめたものであるという認識を 持てたと思います。

開発環境

 Kotlinの開発環境は今回REPLというものを使います。これは、Android Studio 3.0以降がインストールされていれば、使うことができます。 まずは、Android Studioをインストールすることをお勧めします。

Kotlinの基本

 開発環境が整ったらおなじみのHello World!を実行してみます。Javaでは文末にセミコロンが必要でしたが、Kotlinでは省略できます。

println("Hello World!")
実行結果
Hello World!
 それではKotlinの基本について説明していきます。まずは変数と型についてです。 Kotlinでは変数の宣言にvarを使い、定数の宣言にvalを使います。
var 変数名:型=値
val 定数名:型=値
 ただし、Kotlinには型推論の機能があるので、型の指定は省略可能です。 ここで注意なのが、変数や定数への2回目の代入はエラーになるということです。また、Kotlinの基本型はJavaとほとんど同じです。 ただKotlinのString型には、いくつかの特徴的な言語仕様があります。「"""」で囲むことで、複数行の文字列を扱うことができます。 以下にそのコードを示します。
val multiline = """Oh
Hava a
nice Kotlin!"""
println(multiline)
 また、先頭のスペースが不要ならtrimIndent()メソッドが便利です。これを用いることで、文字列の前の不要なスペースをなくすことができます。 文字列に限らず比較には2種類あります。「==」は内容が同じであるかチェックします。「===」は2つのオブジェクトが同じであるかチェックします。 変数名にドル記号$をつけることで、文字列中にテンプレート式を埋め込むことができます。 全く関連のない型への変換には、to型名メソッドが用意されています。これについては以下にコードを示します。
val str = "64"
val intVal = str.toInt()
println(intVal)
実行結果
64
 変数strはString型で定義されています。それをint型にtoInt()を用いて、変換しています。 しかしこういった場合はエラーが生じます。
val str = "Kotlin"
val intVal = str.toInt()
println(intVal)
 これは文字列を数値に変換したときに"Kotlin"は数値ではないので、Nullになるはずなのですが、toInt()のままでは Nullにはなりません。そこで、to型名OrNull()を用います。これを用いることで、プログラムが正常に動作し、Nullになります。 他にも数値から文字列に変換するtoString()メソッドや数値同士の変換するメソッドがあります。これらの基本形はto型名ということだけ強調しておきます。 先ほどNullという言葉が出てきました。これについて深く考えていきます。Nullとはプログラミング言語において何もデータが入っていないことを示します。Kotlinでは、宣言時に 型が指定されたり、型推論により型が確定した変数、定数には、Nullを代入することができません。これをNull非許容型といいます。 そこで、Nullの状態もありうる場合、型名の後ろに?をつけます。これをNull許容型といいます。
var text:String? = null
println(text)
実行結果
null
 ただし、null許容型の変数の場合、様々な制限があります。 具体的に言うと、例えばString?型の変数では、String型が持つメソッドやプロパティ(後述)をそのまま使うことはできません。 このような話はまた別のページでしようと思っていますので、今はそういうものかとくらいに考えておいてください。 続いて話は変わって配列について説明していきます。配列はArrayクラスを使い、arrayOf関数で配列の生成と初期化を行うことができます。
val arrayofInt:Array = arrayOf(1,2,3,4,5,6)
for(i in arrayofInt) print("$i,")
実行結果
1,2,3,4,5,6
 Arrayの後に続けて<>型名を渡しています。これは配列の中に入っているオブジェクトの型を指定しています。この場合はInt型配列であることを示しています。 Arrayクラスは総称型で宣言されており、Arrayの後に続けて<>を渡している値を型引数といいます。また、配列に対しても型推論が機能しますので、型を省略することができます。  次にKotlinの制御構造について説明していきます。

if文の書き方

if(条件式){
条件式がtrueの時に行う処理
}else{

}
 Kotlinのif文は値を返すことができます。以下のような例です。
val a=10
val b=20
val max = if(a>b){
print("a is greater than b")
a
}else{
print("b is greater than a")
b
}
print(max)
実行結果
a is greater than b
20
 値を返す場合には、このように該当するブロックの最後に値を記述しておきます。これは変数でもリテラルでも構いません。 Kotlinにはswitch文は存在せず、次のwhen式を使います。

when文の書き方

when(式){
値1 ->式の結果と値1が一致した場合の処理
値2 ->式の結果と値2が一致した場合の処理
else ->式の結果がどの値とも一致しない場合の処理
}
 このようにwhen式に渡した式の結果により、処理を分岐します。最後のelseには、上記のいずれでもない場合の処理を記述しますが、必須ではありません。 また、値を指定する場合、in演算子に続けて範囲式を記述することで、値の範囲を指定できます。以下にその例を示します。

範囲式を使う

val a=100
when (a){
in 1..10 ->println("a is in the range")
!in 10..20 ->println("a is outside the range")
else ->println("otherwise")
}
 when式もif文同様に、値を返すことができます。when式による型チェックも行うことができます。

型による条件分岐

val a:Any = "Kotlin"
when(a){
is Int->print(a*a)
is String->print(a.toUpperCase())
}
 is演算子というもので型のチェックをしています。Anyはすべてのクラスの親にあたります。また、toUpperCase()は文字列を 大文字に変換するメソッドです。上記ではAny型で宣言したaに文字列を代入し、when式内で型チェックをしています。 if式の代わりにwhen式を使うことも可能です。以下のような例を見てみましょう。

if式のような使い方

val a:Int? = null
val text = "Kotlin"
when{
a != null && text.startsWith("Kot")->println("starts with a prefix 'Kot'")
a == null && text.endsWith("lin")->println("ends with a suffix 'lin'")
else->println("otherwise")
}
 この例のようにwhen式引数を渡さずに、条件分岐をBoolean(trueかfalseをかえすもの)をかえす式とすることで、if文のような書き方をすることができます。 続いてfor文について説明していきます。まず具体例を見てみましょう。

for文

val array = arrayOf(1,2,3,4,10,100)
for(a in array) print("$a,")
実行結果
1,2,3,4,10,100
 このようにしてfor文を使っていきます。ここでは配列の中身をfor文を用いて取り出しましたが、配列に割り振られた番号(添字)を使って 配列を順番に処理をしたい場合は、indicesプロパティというものを使います。以下にその例を示します。

indicesを使ったfor文

val a = arrayOf(1,2,3,4,10,100)
for (i in a.indices){
print("a[$i]=${a[i]}")
}
実行結果
a[0]=1 a[1]=2 a[2]=3 a[3]=4 a[4]=10 a[5]=100
 for文は先ほど出てきた範囲式を用いて、一定回数繰り返すことができます。色々な繰り返し方があるので、その例をいくつか以下に示します。

4回繰り返す

for (i in 1..4){
print(i)
}
実行結果
1234

iの値を減算する

for (i in 4 downTo 1){
print(i)
}
実行結果
4321
 範囲式でカウントダウンするときは、downToを用いればよいです。

stepを使う

for (i in 0..9 step 2){
print(i)
}
実行結果
02468
 範囲式で刻み値を指定して1つ飛ばしや2つ飛ばしをするにはstepを使います。  for分以外にも繰り返しを行えるものはあります。while文do-while文です。以下にその2つの例を示します。

while文

var x=0
while(x<10){
print(x++)
}
実行結果
0123456789

do-while文

var i=7
do{
print("i=${i--}")
}while(i>4)
実行結果
i=7 i=6 i=5
 以上が制御構文の説明でした。

Kotlinのコレクション

話は変わってコレクションというものについて説明します。複数の値をまとめて扱う機能をコレクションといいます。 Kotlinのコレクションでは、読み取り専用のコレクションと変更可能なコレクションの2つが用意されています。変更する必要がない場合は読み取り専用 のコレクションを使うことでバグを排除しコードを読みやすく保つことができます。 まず、リストについて説明します。リストは配列と同じように要素の順番を保持しますが、要素の重複が可能です。 読み取り専用リストの作成と初期化には、listOf関数を使い、変更可能なリストの作成と初期化にはmutabaleListOf関数を使います。

読み取り専用リストを作成

val items:List=listOf(1,2,3)
println(items)
println(items.get[0])
println(items[0])
println(items.size)
実行結果
123
1
1
3
 getメソッドや配列のように[]を使って添字を指定することで、リストから値を取り出すことができます。また、sizeメソッドで リストの長さを知ることができます。

変更可能なリストの作成

val numbers:MutableList=mutableListOf(2,4,6)
println(numbers)
numbers.add(5)
println(numbers)
numbers.remove(4)
println(numbers)
実行結果
246
2465
265
 要素の追加にはaddメソッド、削除にはremoveメソッドを使用します。  次にセットについて説明します。セットは要素の順番を持たず、重複もできません。リスト同様に、読み取り専用と変更可能の2種類が用意されています。

読み取り専用セットの作成

var Strings:set=setOf("A","B","C")
println(set)
実行結果
[A,B,C]

変更可能なセットの作成

var strings:Mutableset=mutableSetOf("A","B","C")
println(strings)
strings.add("y")
println(strings)
strings.remove("A")
println(strings)
実行結果
[A,B,C,y]
[B,C]
 要素の追加、削除はリスト同様addメソッドとremoveメソッドで行います。前述の通り、セットでは値の重複ができませんが、すでにある 要素を追加してもエラーになるわけではありません。addメソッドの戻り値としてfalseが返るだけです。  最後にマップについて説明します。マップはキーと値が対になった要素を持ちます。キーの重複はできないことに注意してください。 今まで同様に、読み取り専用マップと変更可能なマップが存在します。

読み取り専用マップの作成

val fluits:Map=mapOf("apple" to 1,"orange" to 2,"banana" to 3)
print(fluits)
print(fluits.get["apple"])
print(fluits["orange"])
実行結果
(apple=1, orange=2, banana=3)
12
 値の取り出しはgetメソッドの引数にキーを指定するか、配列のように[]内にキーを指定することで可能になります。

変更可能なマップの作成

val fluits:MutableMap=mutableMapOf("apple" to 1,"orange" to 2,"banana" to 3)
print(fluits)
fluits.put("melon",4)
print(fluits)
fluits.remove("banana")
print(fluits)
実行結果
(apple=1, orange=2, banana=3)
(apple=1, orange=2, banana=3, melon=4)
(apple=1, orange=2, melon=4)
 要素を追加するときはputメソッドを使い、削除するときはremoveメソッドを使います。以上Kotlinのコレクションにはlist,set,mapの3つが存在することが わかりました。

関数とラムダ式

 ここでは、Kotlinにおける関数の基本と、Kotlinが持つ関数型プログラミングの機能について説明していきます。 まず関数についてです。以下の例のように関数を定義します。

関数の定義と実行

fun times(a:Int,b:Int):Int{
return a*b
}
times(2,5)
実行結果
res64: kotlin.Int = 10
 これはaとbの積を計算する関数です。この関数は戻り値が存在します。 戻り値とは、関数を実行したときに返ってくる値です。 それでは、戻り値がない場合の関数を見てみましょう。

戻り値のない関数

fun printTimes(a:Int,b:Int):Unit{
print("$a multiplied by $b is ${a*b}")
}
printTimes(2,3)
実行結果
2 multiplied by 3 is 6
 Kotlinでは戻り値のない関数はUnitを使います。これはJavaでいうとvoidに該当します。 また、関数の引数にはデフォルト値を代入することができます。これにより、実行時に引数の指定を省略することができます。 このようなデフォルト値を関数を呼び出す際に、変更することも可能です。その時は、引数名をしっかりと明示することをお勧めします。 次にラムダ式というものです。これから説明するのは、関数に値ではなく、処理を渡せるようにすることで動的に処理を変更する手法のことです。 この時、関数に渡す処理のことをラムダ式といいます。 それでは、具体的にコードを見ていきます。

ラムダ式の代入

var minus={x:Int,y:Int->x-y}
minus(3,2)
実行結果
res66: kotlin.Int = 1
 この時の変数を関数型といいます。このように、引数名を指定した後に、関数に処理してもらいたいことを書きます。 ラムダ式の引数が1つしかない場合は、引数の指定と->の両方が省略可能です。省略した引数は暗黙の引数itとして、 使うことができます。

itを使う

var double:(Int)->Int={it*2}
double(4)
実行結果
res67: kotlin.Int = 8
 以下のように関数の引数に指定することもできます。

関数型の引数を持つ関数の定義と実行

fun doLambda(x:Int,l:(Int)->Int)=l(x)
doLambda(5,{it*2})
実行結果
res68: kotlin.Int = 10
 関数型の引数のメリットは、実行時に処理の内容を決定できるということです。上記では 最初の引数を2番目の引数に従って処理するということだけ決めておいて、どういう処理を行うかは実行時に決定します。

クラスとインタフェース

 KotlinはJavaと強くかかわりがあるので、オブジェクト指向言語でもあります。そのため、クラスやインタフェースを使うことができます。 まずは、クラスの定義方法について説明していきます。Kotlinでは、「private」「protected」「internal」「public」の4つのアクセス修飾子が使われます。 Kotlinのクラスは、デフォルトでfinal(定数、継承禁止)扱いなので、そのままでは他のクラスを継承することができません。 継承させたい場合は、先頭にopenを記述します。また、インナークラス(クラスの中のクラス)を定義し、内側のクラスから外側のクラスにアクセスする場合にはinnerを先頭に記述する必要があります。 クラスのインスタンスを生成する場合は、コンストラクタを実行します。(Javaではnew演算子が必要ですが、Kotlinでは不要) また、インタフェースの定義はinterface インタフェース名(定義)のように記述します。 次に、プロパティについて説明します。クラスで保持したい値をプロパティとして用意します。これは、「セッター」「フィールド」「ゲッター」 の3つで構成されています。セッターは受け取ったデータをフィールドに保存し、ゲッターはデータをフィールドから取り出す役目をしています。 クラスの継承も行うことができます。Kotlinでは、クラスの継承はclass クラス名:親クラス名(引数)で行うことができます。 最後にインタフェースの実装方法を説明します。基本的にクラスの継承と同じです。同時にクラスの継承も行いたい場合は「,」カンマ で区切って並べます。また、Kotlinでは、インタフェースは抽象メソッドだけでなく、具体的なメソッドも定義可能です。 抽象メソッドをオーバーライド(継承先で処理内容を確定すること)際は、override修飾子が必要です。