■目次
Kotlin基本の文法戻る
■Kotlin
■Kotlin入門 JavaプログラマのためのKotlin入門 - Qiita https://qiita.com/koher/items/bcc58c01c6ff2ece658f KotlinでAndroid開発 - Qiita https://qiita.com/paulbarre/items/fb4565c37287d173a78f Android開発を受注したからKotlinをガッツリ使ってみたら最高だった - Qiita https://qiita.com/omochimetaru/items/98e015b0b694dd97f323 正式採用の「Kotlin」で挑戦! 初めてのAndroidアプリ開発 〜ストップウォッチを作ってみよう〜 - エンジニアHub|若手Webエンジニアのキャリアを考える! https://employment.en-japan.com/engineerhub/entry/2017/06/23/110000 最近話題の「Kotlin」は本当に業務に使えるの? ―国内第一人者と「Yahoo!ニュース」Android版開発者が語るKotlin開発実践のコツ:CodeZine(コードジン) https://codezine.jp/article/detail/10730 Kotlin コード規約( Configure style in IDE, Source code organization ) - Qiita https://qiita.com/hmiyado/items/2531be40b3369173a2cd ■Kotlinメモ 【Kotlin入門】配列よりも実用性のあるListの使い方|茶トラネコ日記 https://itneko.com/kotlin-list/ Kotlin のコレクション使い方メモ - Qiita https://qiita.com/opengl-8080/items/36351dca891b6d9c9687 変数を定義する (val, var) | まくまくKotlinノート https://maku77.github.io/kotlin/basic/var.html Android(Java)のあれ、Kotlinでどう書くの? - Qiita https://qiita.com/tporange/items/6cde7a64b4f41072b1c9 Kotlin の Collection まとめ 〜List編〜 - Qiita https://qiita.com/kiririnyo/items/aee905225902d096f7c0 Kotlin のコレクション使い方メモ - Qiita https://qiita.com/opengl-8080/items/36351dca891b6d9c9687 Kotlinの小技 - Qiita https://qiita.com/kichinaga/items/6d57bb868af6a13496c0 [Kotlin] Set型の特徴とList型との相互変換 | プログラミング日和 https://pouhon.net/kotlin-set/1422/ KotlinでMutableSetを作る - Qiita https://qiita.com/mihyaeru21/items/9a293cd1fefd59116751 [Kotlin] List型の特徴とMutableList | プログラミング日和 https://pouhon.net/kotlin-list/1411/ Kotlin のコレクション・配列早見表 - Qiita https://qiita.com/arumati-yk/items/ad2492f481b1f5ed6cdc [android開発] AndroidManifest.xmlの設定一覧(ネットワーク系) - 行け!偏差値40プログラマー http://hensa40.cutegirl.jp/archives/6501 AndroidStudio付属のエミュレータがネットワークに繋がらない時の対処法(API27で動作確認) | takelab.note https://wandering-engineer.tech/2019/08/23/post-4626/ 【正誤情報】『基本からしっかり身につくAndroidアプリ開発入門 Android Studio 3.x対応』|SBクリエイティブ https://www.sbcr.jp/support/14723/ Kotlinで文字列を数値、日付文字列を日付(java.util.Date)、日付を文字列(String)に変換する - Qiita https://qiita.com/emboss369/items/5a3ddea301cbf79d971a Android アプリの ListView で非同期の画像を表示する方法 - Qiita https://qiita.com/tomalatte001/items/345c55603148b1ce41f2 [Android] Picasso でネット上の画像をGridViewで表示 https://akira-watson.com/android/gridview-picasso.html 【Android】picassoでURLから画像をよしなに扱う【Kotlin】 - wrongwrongな開発日記 https://wrongwrong163377.hatenablog.com/entry/2018/09/09/165250 Androidアプリ開発でImageViewを追加する方法【初心者向け】 | TechAcademyマガジン https://techacademy.jp/magazine/3573 Kotlinで初期化を遅延する | RE:ENGINES https://re-engines.com/2018/11/15/kotlin%E3%81%A7%E5%88%9D%E6%9C%9F%E5%8C%96%E3%82%92%E9%81%85%E5%BB... ActivityとFragmentのライフサイクルと罠 - Qiita https://qiita.com/chibi929/items/78f0d3aa2ab4a0229978 Kotlin スコープ関数 用途まとめ - Qiita https://qiita.com/ngsw_taro/items/d29e3080d9fc8a38691e Android はじめてのFragment イベント編 - Qiita https://qiita.com/Reyurnible/items/d6397f5fbb03ee4fb93b ActivityとFragmentの連携を理解する (Android Kotlin) https://101010.fun/posts/android-try-fragment.html android - Fragmentをreplaceしても一つ前のFragmentが残る - スタック・オーバーフロー https://ja.stackoverflow.com/questions/6342/fragment%E3%82%92replace%E3%81%97%E3%81%A6%E3%82%82%E4%B...
■基本の文法
※基本的にPlaygroundで試したもの Kotlin のプレイグラウンド | Android デベロッパー | Android Developers https://developer.android.com/training/kotlinplayground?hl=ja スタンドアロンなKotlinプログラムを実行する方法 - KOMMLOGG https://kommkett.co.jp/memo-kotlin-runenv/ 「Kotlin のプレイグラウンド」で実行する場合、main関数が必要になるので注意 REPL(Read-Eval-Print Loop)を使う場合、AndroidStudioから Tool → Kotlin → Kotlin REPL → HelloAndroid から実行して命令を入力できる ■ハローワールド 以下はPlaygroundの例(main関数が必要)
fun main() { println("Hello, world!") }
以下はREPLの例(main関数が不要)
println("Hello, world!")
■変数
fun main() { // Int型の定数を宣言 val a: Int = 1000 // 型の定義を省略(型推論) val b = 2000 // Int型の変数を宣言 var c: Int = 3000 c = 4000 // 結果を出力 println(a) println(b) println(c) }
■文字列
fun main() { // 文字列を代入 val message1 = "Kotlin" // 複数行の文字列を代入 val multiline = """Hello Kotlin World.""" // 複数行の文字列を代入2(trimMargin()で「|」より前のインデント用スペースを削除) val multiline2 = """Hello |Kotlin |World.""".trimMargin() // 結果を出力 println(message1) println(multiline) println(multiline2) // 文字列テンプレート println("message1 = $message1") println("$message1 length is ${message1.length}") }
■型の変換(キャスト)
fun main() { // 文字列を定義 val message1 = "64" println(message1) // 文字列を数値へ変換 val number1: Int = message1.toInt() println(number1) // 変換に失敗(例外が発生する) /* val message2 = "Kotlin" val number2: Int = message2.toInt() println(number2) */ // 変換できない場合はnullにする(null許容型に格納する) val message3 = "Kotlin" val number3: Int? = message3.toIntOrNull() println(number3) }
■null非許容型とnull許容型
fun main() { // 代入できず「Null can not be a value of a non-null type String」のエラーになる /* var message1: String = null println(message1) */ // null許容の変数を定義する var message2: String? = null println(message2) // null許容型だと、メソッドやプロパティをそのままでは使えない /* val message3: String? = "Kotlin" println(message3.length) */ // nullチェックをした後だと、null非許容型として使える val message4: String? = "Kotlin" if (message4 != null) { println(message4.length) } }
■配列
fun main() { val numbers: Array<Int> = arrayOf(2, 4, 6, 8) println(numbers[0]) println(numbers[1]) println(numbers[2]) println(numbers[3]) }
■条件分岐
fun main() { val a = 10 val b = 20 // 条件分岐 if (a > b) { println("aはbより大きいです。") } else { println("aはbより大きくありません。") } // 値を返す条件分岐 val max = if (a > b) { println("aはbより大きいです。") a // 条件が成立するときに返す値 } else { println("aはbより大きくありません。") b // 条件が成立しないときに返す値 } println(max) }
Kotlinに「switch」文は存在しない。以下のように「when」文を使う
fun main() { val a = 4 when (a) { 1 -> println("aは1です。") 2,3 -> println("aは2もしくは3です。") else -> println("aはそれ以外の値です。") } }
■繰り返し
fun main() { val numbers: Array<Int> = arrayOf(2, 4, 6, 8, 10) // 配列の内容を処理(iには配列の値が入る) for (i in numbers) { println("[$i]") } // 配列の内容を処理(iには配列の添字が入る) for (i in numbers.indices) { println("[$i] = ${numbers[i]}") } // 範囲式を使った繰り返し for (i in 1..4) { println("[$i]") } for (i in 4 downTo 1) { println("[$i]") } for (i in 0..9 step 2) { println("[$i]") } // 条件を満たす間は繰り返し var x = 0 while (x < 10) { println("[$x]") x++ } // 処理を実行してから、条件を満たす間は繰り返し var y = 7 do { println("[$y]") y-- } while (y > 4) }
■コレクション リストは配列と同じように、要素の順番を保持する
fun main() { // 内容を変更できないリストを定義 val items: List<Int> = listOf(2, 4, 6) println(items) /* // リストの内容を変更できない items.add(8) */ }
fun main() { // 内容を変更できるリストを定義 val items: MutableList<Int> = mutableListOf(2, 4, 6) println(items) // リストの内容を変更できる items.add(8) println(items) }
セットは要素の順番を持たず、要素の重複もできない
fun main() { // 内容を変更できないセットを定義 val items: Set<Int> = setOf(2, 4, 6) println(items) /* // セットの内容を変更できない items.add(8) */ }
fun main() { // 内容を変更できるセットを定義 val items: MutableSet<Int> = mutableSetOf(2, 4, 6) println(items) // セットの内容を変更できる items.add(8) println(items) }
マップはキーと値がついになった要素を持ち、要素の重複はできない
fun main() { // 内容を変更できないマップを定義 val items: Map<String, Int> = mapOf("apple" to 1, "orange" to 2, "banana" to 3) println(items) /* // マップの内容を変更できない items.put("melon", 4) */ }
fun main() { // 内容を変更できるマップを定義 val items: MutableMap<String, Int> = mutableMapOf("apple" to 1, "orange" to 2, "banana" to 3) println(items) // マップの内容を変更できる items.put("melon", 4) println(items) }
■関数 関数の定義と実行例
fun main() { val number = times(2, 5) println(number) } fun times(a: Int, b: Int): Int { return a * b }
関数の戻り値が無い場合、戻り値の型に「Unit」を使う (「: Unit」を省略することもできる)
fun main() { hello("Hello!") } fun hello(message: String): Unit { println(message) }
引数には、デフォルト値を指定できる また、呼び出す際に引数名を明示することができる
fun main() { val number = calc(5, c = 4) println(number) } fun calc(a: Int, b: Int=1, c: Int, d: Int=1): Int { return a * b - c / d }
■ラムダ式 関数に渡す式をラムダ式と呼び、ラムダ式を使うと関数を生成して他の関数の引数に渡すことができる 式は必ず「{ 〜 }」で囲う必要がある 処理に return 文は不要で、最後に評価された式が自動で返される ラムダ式が代入される変数は、関数型となる
fun main() { var times = { x: Int, y: Int -> x * y } // 型を明示した場合は以下のようになる /* var times: (Int, Int) -> Int = { x: Int, y: Int -> x * y } */ println(times(2, 5)) }
引数が1つしかない場合、引数の指定と「->」の両方を省略できる 省略した引数は、暗黙の引数「it」として扱うことができる
fun main() { var double: (Int) -> Int = { it * 2 } // 型を明示した場合は以下のようになる /* var double: (Int) -> Int = { it * 2 } var double: (Int) -> Int = { x: Int -> x * 2 } */ println(double(3)) }
関数型を引数に持つ関数も定義できる
fun main() { println(doLambda(5, {it * 2})) } fun doLambda(x: Int, l: (Int) -> Int) = l(x)
■クラス
fun main() { val dog = Dog() dog.name = "Poch" dog.hello() println(dog.name) } class Dog { var name: String = "" fun hello() { println("Hello, $name.") } }
コンストラクタを使うと、クラスからオブジェクトを作成した際に値を初期化できる
fun main() { val dog = Dog("Poch") // プロパティの値を変更できない /* dog.name = "Taro" */ dog.hello() println(dog.name) } class Dog(val name: String) { fun hello() { println("Hello, $name.") } }
コンストラクタは処理を持たないので、値を初期化することしかできない 処理を行いたい場合はイニシャライザを使う
fun main() { val dog = Dog("Poch") dog.hello() println(dog.name) } class Dog(val name: String) { init { println("name: $name") } fun hello() { println("Hello, $name.") } }
クラスを継承すると、親クラスの機能を呼び出すことができる Kotlinのクラスやプロパティはデフォルトでfinalになるため、継承させるにはopenキーワードを指定する必要がある また親クラスから継承したプロパティやメソッドをサブクラス内で使用する場合、override修飾子を付ける必要がある
fun main() { val shibaInu = ShibaInu("Poch") shibaInu.hello() shibaInu.bye() println(shibaInu.name) } open class Dog(open val name: String) { init { println("name: $name") } fun hello() { println("Hello, $name.") } open fun bye() { println("Bye, $name.") } } class ShibaInu(override val name: String): Dog(name) { override fun bye() { println("Bye, $name!") } }
インターフェイスの実装方法は、クラスの継承と同じ メソッドをオーバーライドする場合、override修飾子を付ける必要がある
fun main() { val cat = Cat() cat.eat() cat.showName() } interface Pet { fun eat() fun showName() = println("I'm pet") } class Cat: Pet { override fun eat() = println("I'm eating") }
■型チェックとスマートキャスト AnyはNull非許容型のルートクラス is演算子を使うことで、値の型をチェックできる チェック後は明示的な型の変換(キャスト)が不要で、この機能を「スマートキャスト」と呼ぶ
fun main() { val message = "Kotlin" //val message = 10 var result: Int? = getLength(message) println(result) } fun getLength(obj: Any): Int? { if (obj is String) { return obj.length } else { return null } }
■objectキーワード object宣言を使用することで、シングルトンを実現できる。つまりクラスの宣言とインスタンスの生成を行える (なおJavaにはシングルトンを実現する構文は無く、シングルトンになるように自分でプログラムを作成する必要がある)
fun main() { println(Profile.getData()) } object Profile { var name: String = "Taro Yamada" var age: Int = 24 fun getData(): String { return "$name / $age" } }
companion宣言を使用することで、staticメソッドを実現できる
fun main() { println(Person.showMe()) } class Person(val name: String) { companion object { fun showMe(): String { return "Hello." } } }
以下のようにすると、main関数内で無名インナークラスを定義できる これをオブジェクト式と呼ぶ
fun main() { val x = object { val num = 1 fun showMe(): String { return "Hello." } } println(x.num) println(x.showMe()) }
■SAM変換 ※まだよく理解できていないので、改めて考えたい メソッドを1つしか持たないインターフェイスをSAM(Single Abstract Method)と呼び、 KotlinではSAMインターフェイスを引数としたメソッドを、ラムダ式で置き換えることができるようになっている これをSAM変換と呼ぶ object式を使って書かれたプログラムを例に挙げる 以下は「ボタンをタップした時にメッセージが表示される」というJavaのコード
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { TextView screen = findViewById(R.id.screenView); screen.setText("ボタンがタップされました。"); } });
Kotlinでは以下のように書くことができる setOnClickListenerは引数にView.OnClickListenerインターフェイスを実装したクラスのインスタンスを取るので、 無名インナークラスを使って記述している
button.setOnClickListener { object: View.OnClickListener { override fun onClick(v: View?): Unit { screen.text = "ボタンがタップされました。" } } }
OnClickListenerインターフェイスはonClickしか持たないSAMインターフェイスのため、ラムダ式で書き換えることができる onClickはタップされたViewを受け取り、voidを返すメソッドなので、ラムダの関数型は「(view)->Unit」となる これにより、上のコードは以下のように簡略化できる
button.setOnClickListener ({ v -> screen.text = "ボタンがタップされました。" })
ラムダ式は引数が1つしかなければ、省略して「it」で代用できる
button.setOnClickListener ({ screen.text = "ボタンがタップされました。" })
また関数の最後の引数がラムダ式の場合、それを () の外へ出すことができる
button.setOnClickListener() { screen.text = "ボタンがタップされました。" }
さらにラムダ式が関数の唯一の引数の場合、() すらも省略できる
button.setOnClickListener { screen.text = "ボタンがタップされました。" }
最終的には、上記のような簡素な記述にすることができる ■null安全 オブジェクトの後ろに「?」を付けると、オブジェクトがnullならnullが返却される(安全呼び出し演算子)
fun main() { var s: String? = null //var s: String = "ABC" print("The length of $s is ${s?.length}.") }
以下のようにすると、nullの場合にデフォルト値を返すことができる(エルビス演算子)
fun main() { val message = "Kotlin" //val message = null var result: Int = getLength(message) println(result) } fun getLength(s: String?): Int { return s?.length ?: 0 }
■null許容型のnullチェック null許容型のオブジェクトの中にnullが入っていないことが確実な場合、そのオブジェクトはnull非許容型として扱える
fun main() { val s: String? = "Kotlin" if (s != null) { println(s.length) } }
以下のように「!!」を使うことでも、強制的にnull非許容型として扱える ただし値がnullであった場合はNullPointerExceptionのエラーが発生するので、使用は推奨されない
fun main() { val s: String? = "Kotlin" println(s!!.length) }
■スコープ関数
fun main() { // 通常の書き方 val dog = Dog() dog.name = "Poch" dog.hello() // with関数を使用する with (Dog()) { // ラムダ式内でthisとして参照できるようになる //this.name = "Poch" //this.hello() // さらにthisは省略可能なので以下のように書くことができる name = "Poch" hello() } // apply関数を使用する Dog().apply { // thisを省略可能なのはwith関数と同じ。さらにapply関数は戻り値が対象オブジェクトになる name = "Poch" }.hello() // 通常の書き方(メソッドやプロパティの呼び出しに、毎回「?」を付ける必要がある) val name: String? = "Kotlin" var upper = name?.toUpperCase() var len = name?.length print("$upper $len") // let関数を使用する var output = name?.let { // null許容型を扱いやすくする。対象オブジェクトはitで参照でき、ラムダ式の戻り値がletの戻り値になる var upper = it.toUpperCase() var len = it.length "$upper $len" } print(output) // run関数を使用する(withとletの両方の特性を持つ) var output2 = name?.run { // null許容型を扱いやすくする。thisも省略できる "${toUpperCase()} $length" } print(output2) // also関数を使用する(「対象オブジェクト『もまた』次のように処理して返す」の意味) val arrayInt = listOf(2, 4, 6, 8).also { // 対象オブジェクトはitで参照できる。ラムダ式の戻り値は対象オブジェクトになる print(it) } } class Dog { var name: String = "" fun hello() { println("Hello, $name.") } }
■Pairクラス 2つの値を格納するためのシンプルなクラス 格納した値は、firstプロパティとsecondプロパティで参照できる
fun main() { val p = Pair("apple", 1) println("${p.first} is ${p.second}") }
toを使った構文でPairを作ることもできる これは、mapOfで値を初期化する際にも使用される
fun main() { val p = "apple" to 1 println("${p.first} is ${p.second}") }
■拡張関数 Javaでクラスに機能を追加する場合、クラス内にメソッドを追加するか、クラスを継承してメソッドを追加する必要がある Kotlinでは、クラスの機能をクラスの外側で定義することができる また関数内では、参照元クラスのインスタンスを「this」として扱う
fun main() { println("Kotlin".surround()) } fun String.surround() = "[" + this + "]"