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 + "]"