====== Kotlinプログラミング ====== [[https://www.amazon.co.jp/gp/product/4798160199/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4798160199&linkCode=as2&tag=ipsnekotypeco-22&linkId=abf7623ae11e3edcda307497058d8787| Kotlinプログラミング]] ===== 4.10 Nothing型 ===== TODO():Nothingなので、 - 関数本体でTODO()を使うと何も返さないことを保証する。それでコンパイラはチェックをしない。関数の実装をしなくても、作業ができる。 - TODO()に続くコードは到達不能にできる。 fun shouldReturnAString():String{ TODO("まだ開発途中") println("TODOのした") //到達しない } ===== 15.4 列挙クラス ===== [[kotlin:enumとsealed|]] enum class Direction() { NORTH, EAST, SOUTH, WEST } fun main(){ println("Direction.NORTH = ${Direction.NORTH}") println("Direction.NORTH.name = ${Direction.NORTH.name}") println("Direction.NORTH.ordinal = ${Direction.NORTH.ordinal}") println("Direction.EAST = ${Direction.EAST}") println("Direction.EAST.name = ${Direction.EAST.name}") println("Direction.EAST.ordinal = ${Direction.EAST.ordinal}") }  ↓ Direction.NORTH = NORTH Direction.NORTH.name = NORTH Direction.NORTH.ordinal = 0 Direction.EAST = EAST Direction.EAST.name = EAST Direction.EAST.ordinal = 1 enum class Direction(val x:Int,val y:Int) { NORTH(0,-1), EAST(1,0), SOUTH(0,1), WEST(-1,0) } fun main(){ println("Direction.NORTH.x = ${Direction.NORTH.x}") println("Direction.NORTH.y = ${Direction.NORTH.y}") println("Direction.NORTH = ${Direction.NORTH}") println("Direction.NORTH.name = ${Direction.NORTH.name}") println("Direction.NORTH.ordinal = ${Direction.NORTH.ordinal}") println("Direction.EAST.x = ${Direction.EAST.x}") println("Direction.EAST.y = ${Direction.EAST.y}") println("Direction.EAST = ${Direction.EAST}") println("Direction.EAST.name = ${Direction.EAST.name}") println("Direction.EAST.ordinal = ${Direction.EAST.ordinal}") }  ↓ Direction.NORTH.x = 0 Direction.NORTH.y = -1 Direction.NORTH = NORTH Direction.NORTH.name = NORTH Direction.NORTH.ordinal = 0 Direction.EAST.x = 1 Direction.EAST.y = 0 Direction.EAST = EAST Direction.EAST.name = EAST Direction.EAST.ordinal = 1 ===== 17.1 ジェネリック型を定義する ===== ジェネリック型とはどんな型でも入力として受けとるコンストラクタを持つ型。 Class Class名(xxxx:T) class LootBox(var item:T) class Fedora(val name:String,val value:Int) class Coin(val value:Int) fun main(){ // ジェネリックを使っているのでFedoraでもCoinでもいれることができる val lootBoxOne:LootBox = LootBox(Fedora("general-fedora",15)) val lootBoxTow:LootBox = LootBox(Coin(15)) } ===== 17.2 ジェネリック関数 ===== class LootBox(var item:T){ fun fetch():T{ //関数でもジェネリックが使える return item } } class Fedora(val name:String,val value:Int) class Coin(val value:Int) fun main(){ // ジェネリックを使っているのでFedoraでもCoinでもいれることができる val lootBoxOne:LootBox = LootBox(Fedora("general-fedora",15)) val lootBoxTow:LootBox = LootBox(Coin(15)) lootBoxOne.fetch().run { println("You Retrive $name") } }  ↓ You Retrive general-fedora ===== 17.3 複数のジェネリック型パラメータ ===== fun 関数名():R class LootBox(var item:T){ fun fetch():T{ return item } fun fetch(lootFunction:(T)->R):R{ //戻り値に新しいジェネリックを追加 return lootFunction(item) } } class Fedora(val name:String,val value:Int) class Coin(val value:Int) fun main(){ // ジェネリックを使っているのでFedoraでもCoinでもいれることができる val lootBoxOne:LootBox = LootBox(Fedora("general-fedora",15)) val lootBoxTow:LootBox = LootBox(Coin(15)) //val price = lootBoxOne.fetch({it.value*5}) //val price = lootBoxOne.fetch(){it.value*5} //ラムダの引数は外にだす val price = lootBoxOne.fetch(){it.value*5} //型のは省略できる println(price) } ===== 17.4 ジェネリック型の制約 ===== class LootBox(var item:T){ //とすることでジェネリック型の制約ができる。 fun fetch():T{ return item } } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) //FedoraはLootのサブクラス class Coin(value:Int) // Coinはサブクラスではない fun main(){ val lootBoxOne:LootBox = LootBox(Fedora("general-fedora",15)) //FedoraはサブクラスなのでLoot型の箱に入る。 //val fedora:Fedora = lootBoxOne.item // Error Required:Fedora Found:Loot // Loot型の箱に入れるとFedoraという情報は消える //val lootBoxTwo = LootBox(Coin(15))} // CoinはLootのサブクラスではないので箱にははいらなくなった。←ジェネリック型の制約 ===== 17.6 in と out ===== ジェネリック不変なので下記コードはエラーとなることが大前提! val lootOnebox = LootBox(Fedora("cool-Fedora!",30)) val lootBox:LootBox = lootOnebox //ジェネリックは不変 // このコードはエラー!! class LootBox(val item:T){ // outが必要 } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) //Lootのサブクラス class Coin(value:Int):Loot(value) //Lootのサブクラス fun main() { val coinBox:LootBox = LootBox(Coin(15)) val lootBox1:LootBox = coinBox //ジェネリック型は不変なのでエラー!! LootBox LootBox val lootBox2:LootBox = LootBox(Coin(15)) // これはインスタンスの代入ではないのでoutがなくても可能。 }  ↓ class LootBox(val item:T){ //outをつけるとコンパイルが通る。outにするとvarにはできない。 } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) //Lootのサブクラス class Coin(value:Int):Loot(value) //Lootのサブクラス fun main() { val coinBox:LootBox = LootBox(Coin(15)) val lootBox:LootBox = coinBox // コンパイルできる!が、Coinの情報はなくなりLootになる。 } // 不変によりLootBox型のインスタンスをLootBox型に代入することはできない。 val lootOnebox = LootBox(Fedora("cool-Fedora!",30)) val lootBox:LootBox = lootOnebox //←エラー。outを使う必要がある。 // しかしLootBox型を使ってFedoraの初期値をもつLootBoxのインスタンスを成することはできる。 // 作成した段階で、Fedoraの情報は失われてLootBox型のインスタンスとなる。 val lootBoxThird:LootBox = LootBox(Fedora("cool-Fedora!",30)) // ←正常 ↓スマートキャスト class LootBox(val item:T){ //outをつける!! } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) //Lootのサブクラス class Coin(value:Int):Loot(value) //Lootのサブクラス fun main() { val fedorBox = LootBox(Fedora("cool-Fedora!",30)) var lootBox:LootBox = LootBox(Coin(10)) val myLoot = lootBox.item // Coin型ではなくLoot型にスマートキャストされる lootBox=fedorBox // outがないとLootBxo<Loot>にLootBoxは代入できない。 val myFedora = lootBox.item //LootBoxだが、Fedora型へスマートキャストできる! } class LootBox(item:T){ //inをつけるとvalやvarはつけれれない。プロパティとなり値を返してしまう(生産者となる)から。 // private var loot = item // fun fetch():T{ //inがあるとこのような関数も作成できない // return loot // } } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) //Lootのサブクラス class Coin(value:Int):Loot(value) //Lootのサブクラス fun main() { var fedorBox = LootBox(Fedora("cool-Fedora!",30)) var lootBox:LootBox = LootBox(Coin(10)) fedorBox = lootBox //in があるとLootBox型にLootBxoが代入できる // val myFedora = fedorBox.item //エラー!inがあるので生産できない! } ===== 17.6.補足 in と out ===== そもそも通常のインスタンスの場合は、上位クラス = 下位クラス は可能。 ジェネリック型の場合は、同じレベルのクラス = 同じレベルのクラス が基本(=不変)。 class LootBox(val item:T){ //inをつけるとvalやvarはつけれれない。プロパティとなり値を返してしまう(生産者)から。 } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) class Coin(value:Int):Loot(value) fun main() { // Loot型にcoin型をいれることはできる。 val coinItem:Coin = Coin(15) val LootItem:Loot = coinItem // ジェネリック型にcoin型をいれることはできない。不変! val coinBox = LootBox(Coin(20)) val lootBox:LootBox = coinBox // type mismatchでエラー。LootBoxにする必要がある。  ↓ // キャストすることも可能だが、UnChecked Castと警告がでる。 val lootBox:LootBox = coinBox as LootBox  } outはLootBox型を指定して、LootBox型のインスタンスがつくれないというはなしではない。 もしそうしても、LootBox型のインスタンスが作成されるだけだ。 LootBox型のインスタンスにLootBox型のインスタンスを代入できない。 型なのに中身が型や型になってしまうため。 class LootBox(val item:T){ } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) class Coin(value:Int):Loot(value) fun main() { // outがなくてもLootBox型でLootBox型を作ることはできる。 val lootBox1:LootBox = LootBox(Coin(10)) val coin1:Coin = lootBox1.item // エラー。Coin型の情報はなくなり、Loot型になっている。 val coin2:Coin = lootBox1.item as Coin // キャストしてやれば取り出せる。 //LootBoxでインスタンスを作成したものをLootBox型に変換できない。 val coinBox = LootBox(Coin(20)) val lootBox2:LootBox = coinBox //エラー。 LootBox(val item:T)にする必要がある。  ↓ // outをつけると下記コードは通り、LootBox型からLootBoxへ変換される。 val lootBox2:LootBox = coinBox } ===== 17.7 もっと知りたい? reifiedキーワード ===== ジェネリック型は型消去されて実行時に型情報を利用できない。 class LootBox(val item:T){ } open class Loot(val value:Int) class Fedora(val name:String,value:Int):Loot(value) //Lootのサブクラス class Coin(value:Int):Loot(value) //Lootのサブクラス inline fun randomOrBackupLoot(backupLoot:()->T):T { //reifiedにはinlineが必要 val items = listOf(Coin(14), Fedora("good-fedora", 20)) val randomLoot: Loot = items.shuffled().first() return if (randomLoot is T){ //reifiedがないとTでエラー randomLoot }else{ backupLoot() } } fun main() { randomOrBackupLoot { Fedora("backup-fedora",99) }.run { println("$name") } } ===== 19.3 シーケンス ===== fun generateSequence( seed: T?, nextFunction: (T) -> T? ): Sequence  ↓ generateSequence([初期値],[ラムダ])  ↓ generateSequence([初期値]){[ラムダ]} fun Int.isEven():Boolean{ return this%2==0 } val seq = generateSequence(1) { it+1 }.take(10).filter { it.isEven() }.toList() println(seq) [2, 4, 6, 8, 10] // seedはまずプリントされてから増加する generateSequence(1){it+1}.take(10).forEach { println(it) } 1 2 3 ===== 19.6 チャンレンジ! Mapのキーと値の関係を逆転させる ===== val gradesByStudent = mapOf("Josh" to 4.0,"Alex" to 2.0,"Jane" to 3.0) println(gradesByStudent) val reverseGradesByStudent= gradesByStudent.map { Pair(it.value,it.key) }.toMap() print(reverseGradesByStudent) {Josh=4.0, Alex=2.0, Jane=3.0} {4.0=Josh, 2.0=Alex, 3.0=Jane} ===== 21.11 もっと知りたい? Android KTX と Ankoライブラリ ===== [[https://developer.android.com/kotlin/ktx?hl=ja|Android KTX - Android Developers]] [[https://github.com/Kotlin/anko|Kotlin/anko]] ===== 第22章 コルーチン紹介 ===== ==== 22.4 コルーチンを有効にする ==== build.gradle(Module.app) dependencies { ・・・ //implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.5' // 古い implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2" // android (async, await) implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2' // launch Deferred ・・・ } ==== 22.5 asyncでコルーチンを指定する ==== CharacterGenerator.kt //import kotlinx.coroutines.experimental.Deferred //import kotlinx.coroutines.experimental.async // ↓ import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.GlobalScope // return async ⇒ GlobalScope.async fun fetchCharacterData(): Deferred { return GlobalScope.async { val apiData=URL(CHARACTER_DATA_API).readText() CharacterGenerator.fromApiData(apiData) } } fun fromApiData(apiData:String):CharacterData{ val (race,name,dex,wis,str)= apiData.split(",") return CharacterData(name,race,dex,wis,str) } NewCharacterActivity.kt //import kotlinx.coroutines.experimental.android.UI //import kotlinx.coroutines.experimental.launch // ↓ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch // launch(UI) { ⇒ GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) { characterData = CharacterGenerator.fetchCharacterData().await() displayCharacterDate() }