4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

これが Kotlin の文法か! と感じた場面

Last updated at Posted at 2020-05-06

#はじめに
Kotlinを勉強し始めて1ヶ月ほど経過しました。
Kotlinも他の言語と共通する文法もあり、他の言語を知っていれば1から文法を勉強しなくてもなんとなく読めます。
そんな中でも僕が感じたKotlin特有というかKotlinぽい?というか、そんな記述をまとめてみました。
Kotlinを勉強し始める方は、勉強する前に前にさらっと目を通していただいて雰囲気をつかんでいただけますと幸いです。

#型の後置修飾

var i : Int
val d : Double
val s : String
fun Area(height : Int, width : Int) : Int {return height * width}

 このように整数や浮動小数点、文字列などの変数の方を後置修飾します。また関数の返り値の型も関数名(引数)の後ろに置きます。
 変数には大きく分けてvar変数とval変数があります。val変数は再代入不可、var変数は再代入可です。ごっちゃになりやすいですね。。。基本的には再代入不可のval変数でコードを書いた方がバグの混入が減らせそうです。

#if
 Kotlinのifは文ではなく式です。なのでifを用いて以下のような記述ができます。

fun checkOddNumber(value: Int): Boolean{
    val res: Boolean = if(value % 2 == 0) true else false 
    return res
}

 式なので、これを展開して直接returnすることができます。Kotlinには三項演算子はなく、このようにif式を代替します。

fun checkOddNumber(value: Int): Boolean{
    return if(value % 2 == 0) true else false 
}

 もちろん文っぽくifを用いることもできます。

fun checkOddNumber(value: Int): Boolean{
    if(value % 2 == 0) {
        res = true
    } else {
        res = false
    }
    return res
}

#when
 CやJavaのswitch-case文のような機能です。こちらも式として変数に代入できますし、文として書くこともできます。

val kisetu: String = "夏"
val season: String
season = when(kisetu){
    "春" -> "Spring"
    "夏" -> "Summer"
    "秋" -> "Fall"
    "冬" -> "Winter"
    else -> "Unknown"
}
println(season)
//Summer
val kisetu: String= "冬"
when(kisetu){
    "春" -> {println("Spring")}
    "夏" -> {println("Summer")}
    "秋" -> {println("Fall")}
    "冬" -> {println("Winter")}
    else -> {println("Unknown")}
}
//Winter

#null安全
 Javaではnullの変数に対するメンバ変数/関数にアクセスしようとするとNullPointerExceptionが発生します。Kotlinでは基本的にnullを許容しないので、未然にNullPointerExceptionを防ぐことができます。コンパイル時にnullの変数が存在すればコンパイルエラーとなります。


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

 初期化されていない変数や変数のメソッドやメンバへのアクセスもコンパイルエラーとなります。

val members : Members
members.findByName("Bob")
...
class Members{
   ...
   fun findByName(...){...}
}
//error: variable 'members' must be initialized

 通常、変数は宣言時に初期化します。

val str: String = "Hello world"
println(str)
//OK

 一方、nullを許容する変数も存在します。型名?でnull許容型変数となります。null許容型変数をnullで初期化していれば、error variable 'xxx' must be initializedのエラーは出ません。以下のコードはコンパイルは正常に終了します。

var str: String?
str = null
//OK

 以下のコードで変数のメソッドやメンバを呼ぶ前にnullチェックを行ってくれます。もしnullなら無条件に"null"の文字列を返します。

var str: String?
str = null
println(str?.length) //"null"
//OK

 nullと未初期化状態は区別されます。そのため、null許容型と言っても以下のコードはエラーとなります。

var str: String?
println(str?.length) 
//error: variable 'str' must be initialized

 コンパイラがちょっと厳しいなあと感じることが多々あります。そのぶん出来上がりのコードの安全性はそれなりのものになってると信じたい!

#配列、リスト、マップ
 配列、リスト、マップの宣言の仕方は以下の通りです。
 初期化を行うにはxxxOf()を用います。こちらは読み取りのみできる配列、リスト、マップです。マップに関してはkey to valueの記述で、マップのkeyとvalueを登録します。読み取りのみのものは最初の初期化は値を追加したり、削除することはできません。MutableXxx, mutableXxxOfで読み書きができる配列、リスト、マップの宣言と初期化を行うことができます。

//読み取りのみ
var array : Array<String> = arrayOf("春", "夏", "秋", "冬")
var list : List<String> = listOf("春", "夏", "秋", "冬") 
var map : Map<String, String> = mapOf("春" to "Spring", "夏" to "Summer")
//要素の追加削除変更ができる
var mutalearray : MutableArray<String> = mutableArrayOf(...)
var mutablelist : MutableList<String> = mutableArrayOf(...)
var mutablemap : MutableMap<String, Int> = mutableArrayOf(...)

 (コメントから追記)key to valueに関して、これは言語で定められた構文ではなく、Pair クラスのインスタンスを生成する to 関数 の呼び出しです。 Kotlin では二項演算子のように使える中置関数を定義することができます。
 例えば以下のように宣言しておいて、リストに値を追加しようとしてもコンパイルエラーとなります。

var season : MutableList<String>
season.add("春")
//variable 'season' must be initialized

seasonには初期化しない限りnullだからです。初期値は入れないけど、後で値を入れる想定なら、mutableListOfで空のリストで初期化します。

var season : MutableList<String> = mutableListOf()
season.add("春")

#forforEach
kotlinではforで指定数の繰り返し処理や、配列やリスト、マップの各要素に任意の処理をすることができます。

val season : Map<String, String> = mapOf("春" to "Spring", "夏" to "Summer", "秋" to "Fall", "冬" to "Winter")
for (elem in season){
    println("${elem.key} ${elem.value}")
}
var sum : Int = 0
for (i in 1..10){
  sum += i
}
//iを2ずつ加算してループを実行
var odd_sum : Int = 0
for (i in 1..10 step 2){
  odd_sum += i
}

forEachは配列やリスト、マップのメソッドで、ほぼforと同じことができます。

val season : Map<String, String> = mapOf("春" to "Spring", "夏" to "Summer", "秋" to "Fall", "冬" to "Winter")
season.forEach({
  elem -> println("${it.key} ${it.value}")
})

 forEachでは各要素に対して処理したい内容の関数を引数にとります。このように引数に関数をとる関数を高階関数と呼びます。また引数の関数はラムダ式で記述されることが多いです。
#型推論
Kotlinでは変数の型を明示しなくても、初期化する値によって型を自動で定義してくれます。

val num1 = 10 //Int
val num2 = 9.8 //Double
val str = "Hello" //String
val season = mapOf("春" to "Spring", "夏" to "Summer", "秋" to "Fall", "冬" to "Winter") //Map<String, String>

静的型付けの恩恵を受けつつ、コードもすっきりします。
#ラムダ式と高階関数
 試しに引数で当てられた関数の実行時間を計測する高階関数(measureTime)を作ってみます。

//UnitはC言語でいうvoid型みたいなもの
fun measureTime(targetFunction: (Int) -> Unit) : Long{
    val start = System.currentTimeMillis()
    targetFunction(1000000)
    val end = System.currentTimeMillis()
    return end - start
}

fun measureTime(targetFunction: (Int) -> Unit) : Long
この部分、関数引数の指定の仕方は、関数名 : (引数, ...) -> 返り値の型です。
計測する関数は以下の関数とします。

fun target(loop){
   var x = 0
   for(i in 1..loop) {x++}
}

この関数を引数に入れます。
関数へは::を付けることで参照できます。

var time : Int = measureTime(::target)
println(time)
//3(ex)

このtarget関数はラムダ式(無名関数)で記述できます。

var time : Long = measureTime({loop : Int -> 
    var x = 0
    for(i in 1..loop) {x++}
})
println(time)
//3(ex)

{引数,... -> 処理内容} でラムダ式を記述します。関数の最後の引数がラムダ式の場合、ラムダ式は関数の()の外に出せます。この例では引数がラムダ式のみなので、ラムダ式が最初の引数であって最後の引数です。()には残りの変数の引数が入り得ます。

var time : Long = measureTime(){loop : Int -> 
    var x = 0
    for(i in 1..loop) {x++}
}

また関数の引数がラムダ式だけの場合、関数の()も省略できます。結局引数がラムダ式1つだけの場合は以下のように()を省略できます。

var time : Long = measureTime{loop : Int -> 
    var x = 0
    for(i in 1..loop) {x++}
}

同様にしてforEachも()を省略できます。

season.forEach{ elem ->
  println("${elem.key} ${elem.value}")
}

 (コメントを受けて)暗黙的に仮引数が1つしかないラムダ式の引数はitとして定義されます。forEachでは各要素は変数itに入れられ、以下のように書けます。

season.forEach{
  println("${it.key} ${it.value}")
}

すっきりしましたね!

#最後に
Kotlinの特徴的な文法などをまとめてみました。
今回は基礎部分だけだったのでこれの続編もそのうち書こうと思います。
コメントくださった方ありがとうございます!

#参考文献
速習Kotlin(Kindle) 著:山田祥寛

4
3
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?