Android
Kotlin
入門
アプリ開発

【Android】アプリエンジニアのためのKotlin入門(基本文法編)

Kotlin入門

Googleが2017/5/18のGoogle I/OにてKotlinをサポートすると発表してから半年が経ち、Android開発の話題にKotlinは欠かせない存在となりました。本記事はそんなKotlinを使ってみたいと思っている人のための入門用の記事になります。
QiitaにはKotlin入門の記事がいくつかありますが、本記事は
・「個人の感想はなるべく含まずKotlinの基本的な部分を網羅的にまとめる」
ことを意識して書かれています。
また、特にアプリエンジニアを対象とした入門記事であり、その中の"基本文法編"です。近いうちに続編を投稿予定です。
基本的には以下のような構成で書かれています。
image.png

(サンプルコードに関してはKotlin.REPL、Android Studioで作成したコードを使用しています。)

ではでは本題です。

変数とデータ型

var :代入可能な変数
val :定数

var text = "hello"
text = "world"  // ok

val name = "Kot"
name = "Lin"  // × val cannot be reassigned(再代入無理だよ〜)

また、型推論が適用されているので、型の宣言の省略が可能です。

オプショナル型

nullを許容させるには宣言が必要です。?をつけます。

var text :String = null
// error: null can not be a value of a non-null type String

var text: String? = null
//これならok

オプショナル型の変数へのアクセスには ?!!が使えます。
nullの可能性があるなら?を使用します。
nullでないことが保証されるなら!!を使用します。

var text: String?  = null
text.length
// 何もつけずに.lengthを使おうとするとエラーが起こる
// error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

text!!.length
// !!だとnullの場合はアクセスできない kotlin.KotlinNullPointerException

text?.length
// ?の場合はnullのときはnullが出力される

var text: String? = "hello"

text!!.length
// 出力結果 5

text?.length
// 出力結果 5

また,?:を用いることでnullの時の値を指定することができます。

var text: String? = null
var textLength = text?.length ?: -1

textLength
// nullなので-1が出力される

if文

Kotlinでのif文は次のように記述します。

var number = 8
var result: String? = null


if (number < 5) {
    result = "small"

} else if (number > 5 && number <= 10){
    result = "medium"

} else {
    result = "large"
}

result
// medium が出力される

上記の記述において result に値を代入している箇所を省略して以下のようにも書くことができます。
「変数名」 + 「=」 + 「if文」

result = if (number < 5) {
    "small"

} else if (number > 5 && number <= 10){
    "medium"

} else {
    "large"
}

また、if文の条件でnullチェックをしてnullでないことが保証される場合はオプショナル型の変数であっても?なしでメソッドを使用することができます。

val text: String? = "hello world"

if (text != null){
    text.length
}

補足:上記コードにおいて,textvalではなくvarで定義していた場合は以下のようにエラーが生じます。

var text: String? = "hello world"

if (text != null){
    text.length
    // error: smart cast to 'String' is impossible, because 'text' is a mutable property that could have been changed by this time
}

これはnullチェックをしたあとで、別スレッドにおいてtextの値が(varであるが故に)書き換えられ、nullの状態でlengthを使用される可能性があるためです。

when文

when文は以下のような条件分岐が可能です。

var number = 8
var text: String? = null

when (number){
    1 -> text = "number is one"
    in 2..5 -> text = "small"
    in 6..10 -> text = "large"
    else -> text = "not valid number"
}

text
// largeが出力される

! で条件を否定することが可能なことに加え、breakが暗黙的に働いています。

var number = 8
var text: String? = null

when (number){
    1 -> text = "number is one"
    !in 2..5 -> text = "small" // この行で処理終了
    in 6..10 -> text = "large" // この条件にも当てはまるが通らない
    else -> text = "not valid number"
}

text
// smallが出力される

また、if文の時と同様に変数への値の代入は省略して書くことが可能です。

text = when (number){
    1 -> "number is one"
    !in 2..5 -> "not small"
    in 6..10 -> "large"
    else -> "not valid number"
}

when() の()を省略し、条件式を->の左に記述することもできます。

var a = 5
var b = 10
var text: String? = null

when {
    a < 5 -> text = "a is small"
    a >= 5 && b >= 5 -> text = "number is not small" 
}

text
// number is not small が出力される

コレクション

Array型:Kotlin の Array は Java でいう配列。List, Map, Set などのデータの集合を扱うインターフェースが用意されています。
List型 :順序のあるコレクション。
Set型  :順序のないコレクション。重複する値は削除される。
Map型  :辞書型。キーと値をセットで保持するコレクション。

読み取り専用(read-only)とmutableなコレクションがあります。
読み取り専用のコレクション宣言は:val list = listOf(1,2,3)
のように ~Of で表されます。(他のコレクションも同様です)
mutableなコレクションの宣言は:
var mutableList = mutableListOf(1,2,3)のように mutable が先頭につきます。

val list = listOf(1,2,3,4,5)

list.add(6) // エラー、add使えない

var mutableList = mutableListOf(1,2,3,4,5)

mutableList.add(6) // mutableならaddを使える
// mutableList の出力結果は [1, 2, 3, 4, 5, 6]

読み取り専用はその名の通り、addやsetを持ちません。が、immutableであることを保証したものではありません。以下のように読み取り専用の変数にmutableな値が代入され(MutableListはListを継承しているので代入可能)値が変わる可能性があります。

var mutableList = mutableListOf(1,2,3,4,5)
val readOnlyList: List<Int> = mutableList

mutableList.add(6)

// readOnlyList の出力結果は [1, 2, 3, 4, 5, 6]

mapはPairやtoを用いて値をセットすることができます。

val map1 = mapOf(Pair(1, "one"), Pair(2, "two"))
// map1の出力結果は {1=one, 2=two}

val map2 = mapOf(1 to "one", 2 to "two")
// map2の出力結果は {1=one, 2=two}

そして、コレクションに対してto~メソッドを使用することによりコレクションの型を変更することも可能です。

var set1 = setOf(1,1,2,2,3,3)

// set1 は [1, 2, 3]

val mutableList  = set1.toMutableList()
mutableList.add(1)
// mutableの出力結果は[1, 2, 3, 1]

forループ

条件に数字を渡せばその範囲で処理を行いますし、文字列を渡せば1文字ずつ取り出し、コレクションを渡せば1要素ずつ取り出して処理を行います。
downToで値の減少、stepで減少量を設定できます。

for (i in 1..10) {

    print("$i,")
}
// 1,2,3,4,5,6,7,8,9,10,

for ( i in 10 downTo 1){
    print("$i,")
}
// 10,9,8,7,6,5,4,3,2,1,

for ( i in 10 downTo 1 step 3){
    print("$i,")
}
// 10,7,4,1,

while文

javaやswiftと比べて真新しい書き方はありません。

while (i <= 10) {
    print("$i")
    i++
}
// 12345678910

var i  = 1

do {
    print("$i")
    i++
} while (i <= 10)
// 12345678910

関数

関数はfunを使用して表され、デフォルトはpublicです。

fun checkSmallNumber (number: Int) : Boolean {
    return number <= 5
}

checkSmallNumber(6)
// false

checkSmallNumber(3)
// true

また、{}を省略して=の右側に記述することが可能です。

// {}省略型(戻り値ありの場合)
fun checkSmallNumber (number: Int) : Boolean = number <= 5

checkSmallNumber(1)
// true


// {}省略型(戻り値なしの場合)
fun checkSmallNumber (number: Int) = number - 5

var result = checkSmallNumber(10)

result
// 5

vararg(可変長引数)を指定することで、引数で受け取れる値を可変にできます。
また、ラムダ式は以下のように書けます。
val sum = { x: Int, y: Int -> x + y }

fun checkSmallNumber (vararg numbers: Int): Boolean {

    return numbers.any { number -> number <= 5 }
}

checkSmallNumber(1,2,3,4,10)
// true

補足:.anyは配列に対して用いることができラムダ式を引数に取ることができます。そして、要素を一つずつ取り出して処理を行い1つでも一致すれば(trueであれば)trueを返します。

引数にデフォルト値を設定することもできます。

fun concat (stringList: List<String>, separator: String = ",") = stringList.joinToString(separator)

// separatorはデフォルト値を使用
concat(listOf("Kotlin", "Android", "Android Studio"))
// 出力結果
 Kotlin,Android,Android Studio

// separatorの書き換え
concat(listOf("Kotlin", "Android", "Android Studio"), separator = "/")
// 出力結果
 Kotlin/Android/Android Studio


おわりに

以上がKotlinの基本文法です。 この記事がKotlinの理解の一助となれば幸いです。今後は関数型プログラミング、オブジェクト指向プログラミングとしてのKotlinについて、KotlinでのAndroidアプリ開発などなど書いていければと思っています!