ユーザ用ツール

サイト用ツール


サイドバー

kotlin:kotlinプログラミング

Kotlinプログラミング

4.10 Nothing型

TODO():Nothingなので、

  1. 関数本体でTODO()を使うと何も返さないことを保証する。それでコンパイラはチェックをしない。関数の実装をしなくても、作業ができる。

- TODO()に続くコードは到達不能にできる。

fun shouldReturnAString():String{
    TODO("まだ開発途中")
    println("TODOのした") //到達しない
}

15.4 列挙クラス

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名<T>(xxxx:T)

class LootBox<T>(var item:T)
 
class Fedora(val name:String,val value:Int)
class Coin(val value:Int)
 
fun main(){
    // ジェネリックを使っているのでFedoraでもCoinでもいれることができる
    val lootBoxOne:LootBox<Fedora> = LootBox(Fedora("general-fedora",15))
    val lootBoxTow:LootBox<Coin> = LootBox(Coin(15))
}

17.2 ジェネリック関数

class LootBox<T>(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<Fedora> = LootBox(Fedora("general-fedora",15))
    val lootBoxTow:LootBox<Coin> = LootBox(Coin(15))
 
    lootBoxOne.fetch().run {
        println("You Retrive $name")
    }
}
 ↓
You Retrive general-fedora

17.3 複数のジェネリック型パラメータ

fun <R> 関数名():R

class LootBox<T>(var item:T){
    fun fetch():T{ 
        return item
    }
    fun <R> 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<Fedora> = LootBox(Fedora("general-fedora",15))
    val lootBoxTow:LootBox<Coin> = LootBox(Coin(15))
 
    //val price = lootBoxOne.fetch<Int>({it.value*5}) 
    //val price = lootBoxOne.fetch<Int>(){it.value*5}  //ラムダの引数は外にだす
    val price = lootBoxOne.fetch(){it.value*5}  //型の<Int>は省略できる
    println(price)
}

17.4 ジェネリック型の制約

class LootBox<T:Loot>(var item:T){  //<T>を<T:Loot>とすることでジェネリック型の制約ができる。
    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<Loot> = 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<Loot> = lootOnebox //ジェネリックは不変
// このコードはエラー!!
class LootBox<T>(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<Coin> = LootBox(Coin(15))
    val lootBox1:LootBox<Loot> = coinBox //ジェネリック型は不変なのでエラー!! LootBox<Loot> LootBox<Coin>
    val lootBox2:LootBox<Loot> = LootBox(Coin(15)) // これはインスタンスの代入ではないのでoutがなくても可能。
 
}

 ↓

class LootBox<out T>(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<Coin> = LootBox(Coin(15))
    val lootBox:LootBox<Loot> = coinBox // コンパイルできる!が、Coinの情報はなくなりLootになる。
 
}
  // 不変によりLootBox<Fedora>型のインスタンスをLootBox<Loot>型に代入することはできない。
  val lootOnebox = LootBox(Fedora("cool-Fedora!",30))
  val lootBox:LootBox<Loot> = lootOnebox //←エラー。outを使う必要がある。
  
  // しかしLootBox<Loot>型を使ってFedoraの初期値をもつLootBoxのインスタンスを成することはできる。
  // 作成した段階で、Fedoraの情報は失われてLootBox<Loot>型のインスタンスとなる。
  val lootBoxThird:LootBox<Loot> = LootBox(Fedora("cool-Fedora!",30)) // ←正常

↓スマートキャスト

class LootBox<out T>(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<Loot> = LootBox(Coin(10))
 
    val myLoot = lootBox.item // Coin型ではなくLoot型にスマートキャストされる
 
    lootBox=fedorBox  // outがないとLootBxo<Loot>にLootBox<Fedora>は代入できない。
    val myFedora = lootBox.item //LootBox<Loot>だが、Fedora型へスマートキャストできる!
}
class LootBox<in T>(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<Loot> = LootBox(Coin(10))
 
    fedorBox = lootBox //in があるとLootBox<Fedora>型にLootBxo<Loot>が代入できる
    // val myFedora = fedorBox.item //エラー!inがあるので生産できない!
}

17.6.補足 in と out

そもそも通常のインスタンスの場合は、上位クラス = 下位クラス は可能。
ジェネリック型の場合は、同じレベルのクラス = 同じレベルのクラス が基本(=不変)。

class LootBox<T>(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<Loot> = coinBox // type mismatchでエラー。LootBox<out T>にする必要がある。
     ↓
    // キャストすることも可能だが、UnChecked Castと警告がでる。
    val lootBox:LootBox<Loot> = coinBox as LootBox<Loot> 
}

outはLootBox<Loot>型を指定して、LootBox<Coin>型のインスタンスがつくれないというはなしではない。
もしそうしても、LootBox<Loot>型のインスタンスが作成されるだけだ。

LootBox<Loot>型のインスタンスにLootBox<Coin>型のインスタンスを代入できない。
<Loot>型なのに中身が<Coin>型や<Fedora>型になってしまうため。

class LootBox<T>(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<Loot>型でLootBox<Coin>型を作ることはできる。
    val lootBox1:LootBox<Loot> = LootBox(Coin(10)) 
    val coin1:Coin = lootBox1.item  // エラー。Coin型の情報はなくなり、Loot型になっている。
    val coin2:Coin = lootBox1.item as Coin // キャストしてやれば取り出せる。
 
    //LootBox<Coin>でインスタンスを作成したものをLootBox<Loot>型に変換できない。
    val coinBox = LootBox(Coin(20)) 
    val lootBox2:LootBox<Loot> = coinBox //エラー。 LootBox<out T>(val item:T)にする必要がある。
     ↓
    // outをつけると下記コードは通り、LootBox<Coin>型からLootBox<Loot>へ変換される。
    val lootBox2:LootBox<Loot> = coinBox
}

17.7 もっと知りたい? reifiedキーワード

ジェネリック型は型消去されて実行時に型情報を利用できない。

class LootBox<T>(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 <reified T> 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 <T : Any> generateSequence(
    seed: T?,
    nextFunction: (T) -> T?
): Sequence<T>
 ↓
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ライブラリ

第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<CharacterData> {
        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()
            }
kotlin/kotlinプログラミング.txt · 最終更新: 2019/12/10 08:10 by ips