『第1行コード』第3版のKotlinプログラミング入門下編(三)


2.6.LambdaプログラミングJavaは1.8後にLambdaプログラミング構文のサポートを追加し、Kotlinは最初からサポートしています.高次関数、DSLなどの高度なLambdaテクニックを紹介します.2.6.1.集合の作成と従来の意味での遍歴の集合には,List(ArrayList,LinkedList),Set(HashSet),Mapキー値対(HashMap)構造がある.需要は、多くのフルーツ名を含むコレクションを作成し、KotlinでArrayListインスタンスを作成し、次のようにフルーツを1つずつ追加することです.
fun main(){
    val list = ArrayList()
    list.add("Apple")
    list.add("Banana")
    list.add("Orange")
    list.add("Pear")
    for (fruit in list){
        println(fruit)
    }
}

この初期化集合方式は煩雑であり,Kotlinは集合を初期化するために組み込まれたlistOf関数を提供する.
 val list2 = listOf("Apple", "Banana", "Orange", "Pear")

しかし、この方法では可変の集合が作成され、調べるしかなく、削除することはできません.Kotlinは不変性の面で極めて厳格に制御され,可変集合mutableListOf関数はこの問題を解決した.
 val list3 = mutableListOf("Apple", "Banana", "Orange", "Pear")
 list3.add("WaterMelon")

Setの使用法はリストとほぼ同じであり,集合を作成する方法をsetOf()とmutableSetOf()関数に変えただけである.Set下位層はhashマッピングメカニズムに基づいてデータを格納し,秩序を保証できない.Mapセットはキー値ペア形式のデータ構造であり、従来のMapはMapインスタンスを作成し、キー値ペアをMapに追加する.次のようになります.
    val map = HashMap()
    map.put("Apple", 1)
    map.put("Banana", 2)
    map.put("Orange", 3)
    map.put("Pear", 4)
    map.put("Grape", 5)

しかし、実際にはKotlinはこの方法をあまり提案していないが、配列の下付き文字のような文法構造を打ち出した.
    val map = HashMap()
    map["Apple"] = 1
    map["Banana"] = 2
    map["Orange"] = 3
    val num = map["Apple"]//      

もちろんこれは依然として最も簡単ではありません.KotlinはmapOf()とmutablemapOfを提供してMapの使い方を簡略化します.ここではtoを用いて関連付けを行うのではなくinfix関数を用いた.
    val map = mapOf("Apple" to 1, "Banana" to 2, "Pear" to 3)
    for ((fruit, number) in map) {
        println("fruit is " + fruit + ",number is " + number)
    }

2.6.2.集合の関数式APIの需要は以下の通りです:果物の集合の中で単語が最も長い果物を見つけますか?コードは次のとおりです.
fun main() {
    val list = listOf("Apple", "Banana", "Orange", "Pear", "WaterMelon", "Grape")
    var maxLengthFruit = ""
    for (fruit in list) {
        if (fruit.length > maxLengthFruit.length) {
            maxLengthFruit = fruit
        }
    }
    println("max length fruit is " + maxLengthFruit)
}

実は集合の関数式APIを使用すると、この機能がより容易になります.
val maxLengthFruit = list.maxBy { it.length }  

これを説明します.Lambdaはパラメータとして伝達できるコードです.通常、パラメータは変数を伝達しますが、今は確かに小さなコードです.構文は次のとおりです.
{   1:    ,   2:    ->   }

そこで、さっきの問題は文法構造に従って適用されました.
val lambda = { fruit: String -> fruit.length }
val maxLengthFruit = list.maxBy (lambda )

まず簡略化を開始し,lambda変数を特別に定義する必要がなく,直接伝達する.
val maxLengthFruit = list.maxBy ({ fruit: String -> fruit.length } )

次にKotlinは,最後のパラメータを関数括弧の外側に移動することができ,Lambdaパラメータが関数一意のパラメータであれば括弧を省略することができると規定している.
val maxLengthFruit = list.maxBy { fruit: String -> fruit.length }

タイプ導出メカニズムに基づいて、パラメータリストは、導出可能であるため、パラメータタイプを宣言する必要はありません.
val maxLengthFruit = list.maxBy { fruit -> fruit.length }

最後に、パラメータリストにパラメータが1つしかない場合は、パラメータ名を宣言する必要はなく、itキーを使用して置き換えます.だから
val maxLengthFruit = list.maxBy { it.length }  

関数式APIは今ではあまりよくありませんが、すべての果物名を大文字にする練習をしてみましょう.Map関数は各要素を別の値にマッピングでき、マッピング規則はLambdaで指定されます.
val newList = list.map { it.toUpperCase() }

これに基づいて5文字以内の果物だけを残し,filter関数を用いて実現できる.まずfilterからmapの方が効率的です.
val newList = list.filter { it.length <= 5 }.map { it.toUpperCase() }

最後に、一般的な関数式API:anyとall関数について説明します.Anyは,集合中に少なくとも1つの要素がこの条件を満たすか否かを判断し,all関数は集合中のすべての要素がこの条件を満たすか否かを判断するために用いられる.
    val list = listOf("Apple", "Banana", "Orange", "Pear", "WaterMelon", "Grape")
    val anyResult = list.any { it.length <= 5 }
    val allResult = list.all { it.length <= 5 }
    println("anyResult " + anyResult + ",all Result " + allResult)

出力結果は次のとおりです.
anyResult true,all Result false

2.6.3.Java関数式APIの呼び出しKotlinでJavaメソッドを呼び出すには関数式APIも使用できますが、制限があります.具体的には、関数APIを使用するには、KotlinがJavaメソッドを呼び出すと、Java単一抽象メソッドインタフェースパラメータ(1つのみ、複数は使用できません)を受け入れます.JavaネイティブAPIのRunnableインタフェースには、次のように定義された実装されるrunメソッドがあります.
public interface Runnable{
    void run();
}

サブスレッドを作成するコードはRunnableパラメータを受け入れ、Javaは次のようになります.
       new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println("Thread is ruuning");
           }
       }).start();

Kotlinコードはnewキーワードを捨て、匿名の内部クラスインスタンスを作成するにはnewを使用できません.objectキーワードに変更します.
    Thread(object :Runnable{
        override fun run() {
            println("Thread is running")
        }
    }).start()

簡略化すると、まずrunメソッドを明示的に書き換える必要はありません.Kotlinは、Runnableの後のLambda式がrunメソッドでコンテンツを実現することを知っているからです.
   Thread(Runnable {
        println("Thread is running")
   }).start()

また,1つのJavaメソッドのパラメータリストに1つ以上のJava単一抽象メソッドインタフェースパラメータが存在しない場合は,インタフェース名を省略することができる.
  Thread({
        println("Thread is running")
  }).start()

最後に、このLambda式が最後のパラメータである場合、式を括弧の外に移動することができます.方法は唯一のパラメータしかないので、括弧も省略できます.最後のコードは以下のようになります.
   Thread({
        println("Thread is running")
   }).start()

これはよくありますが、Androidには次のように定義されたクリックイベントインタフェースonClickListenerがよく使われています.
public interface OnClickListener{
    void onClick(View v)
}

このインタフェースは単一抽象メソッドインタフェースであり,ボタンbuttonインスタンスはイベント登録をクリックする際にJavaでこのように書くことができる.
button.setOnClickListener(new View.OnClickListener(){
   @Override
   public void onClick(View v){
   }
});

同様の機能をKotlinで実現することで,関数式API書き方を用いてコードを簡略化することができる.コードがずいぶん簡素化されている.
button.setOnClickListener{}

注意しなければならないのは、Java関数APIの使用はKotlinがJavaメソッドを呼び出すことに限られ、単一抽象メソッドインタフェースはJavaによって定義されなければならないことです.Kotlinには、より強力な自動定義関数式API機能を実現するための専門的な高次関数があるからです.2.7.空のポインタAndroidクラッシュで最も高い異常タイプは、空のポインタ異常であり、プログラマーを受けない可能性があります.2.5.3節で述べたJavaコードを見てみましょう.
public void doStudy(Study study) {
    study.readBooks();
    study.doHomeWork();
}

問題が発生しやすく、doStudyに伝達されたnullパラメータであれば、空ポインタ異常が発生し、より妥当な方法は、判定処理を行うことです.次のようになります.
public void doStudy(Study study) {
    if(study!=null){
       study.readBooks();
       study.doHomeWork();
   }
}

2.7.1.使用可能なタイプのシステムKotlinは、空のポインタの異常チェックを実行時ではなくコンパイル時に繰り上げるため、空のポインタの異常はほとんど発生しません.このコードは問題ありません.
fun doStudy(study: Study) {
    study.readBooks()
    study.doHomeWork()
}

ビジネスロジックがパラメータまたは変数を空にするように要求されているとしたらどうしますか?Kotlinは空のタイプのシステムを提供していますが、空のシステムを使用する場合は、コンパイル時に潜在的な空のポインタをすべて異常に処理してコンパイルする必要があります.空のタイプシステムは、クラス名の前に疑問符を付けます.eg:Intは空ではない整数型を表し、Int?空の整数を表す.疑問符を付けると、次の2つの方法で空のポインタが異常になる可能性があるため、空を判断する必要があることがわかりました.最後のコードは次のとおりです.
fun doStudy(study: Study?) {
    if (study != null){
        study.readBooks()
        study.doHomeWork()
    }
}

If判定文は煩雑であり、グローバル変数の判定問題を処理できないため、Kotlinはいくつかの列補助ツールを提供してより簡単な判定処理を行う.2.7.2.判空補助ツールはまず勉強しますか?のオペレータは、オブジェクトが空でない場合に通常呼び出されるメソッドであり、オブジェクトが空である場合に通常呼び出されるメソッドです.上記のコードは以下のように簡略化できます.
fun doStudy(study: Study?) {
    study?.readBooks()
    study?.doHomeWork()
}

次に勉強しますか.オペレータは、左右に1つの式を受け入れ、左の式の結果が空でない場合は左の式の値を返します.そうでない場合は、右の式の値を返します.例として、
 val c = if (a != null) {
        a
    else{
            b
        }
}

簡略化すると次のようになります.
val c = a ?: b

次に具体的な例を組み合わせて使用しますか?のと?:2つのオペレータで、1つの関数を作成してテキストの長さを取得します.コードは次のとおりです.
fun getTextLength(text: String?): Int {
    if (text != null) {
        return text.length
    }
    return 0
}

オペレータを使用すると、より簡単にできます.
//  length     ?.   ,         。
fun getTextLength1(text: String?) = text?.length ?: 0

Kotlin空ポインタ検出メカニズムは必ずしもそうではなく、使用可能!!オブジェクトの後ろに追加すると、コンパイルを強制的に通過しますが、推奨されません.最後に、補助ツールlet関数について説明します.関数APIのプログラミングインターフェースを提供し、元の呼び出しオブジェクトをパラメータとしてLambda式に渡します.サンプルコードは次のとおりです.
 obj.let{obj2 -> //    }

objオブジェクトのlet関数はすぐにLambda式のコードを実行し、objオブジェクト自体がパラメータとして入力されるので、重複を防ぐためにパラメータをobj 2に変更しますが、実際には同じオブジェクトです.さっきdoStudy関数でこのようなコードを得ました.
fun doStudy(study: Study?) {
    study?.readBooks()
    study?.doHomeWork()
}

しかし、表現がうるさいので、if判定を2回行う必要があります.使用を考える?letと最適化します.最適化コードは以下の通りです.
fun doStudy(study: Study?) {
    //?.          let  
    //let    study         Lambda    ,       ,      。
    study?.let { stu ->
        stu.readBooks()
        stu.doHomeWork()
    }
}

Lambda式のパラメータリストにパラメータが1つしかない場合は、パラメータ名を宣言せずにitキーを直接使用できます.最終コードは次のとおりです.
fun doStudy(study: Study?) {
    study?.let { 
        it.readBooks()
        it.doHomeWork()
    }
}

説明する必要があるのは、letはグローバル変数の判定問題を処理することができ、ifはそれを行うことができないことである.たとえば、次のコードがあります.
//                ,             ,        ,     ,let    。
var study: Study? = null

fun doStudy1(){
    if (study != null){
        study.readBooks()
        study.doHomeWork()
    }
}

2.8.Kotlinの中の小さいマジック2.8.1.文字列埋め込み式KotlinはJavaのように馬鹿なつづり文字列ではなく、文字列埋め込み式${}の構文規則を使用しています.
    val str = "hello,${obj.name}.nice to meet u"
        ,            。                :
    val brand = "Samsung"
    val price = 1299.99
    println("Cellphone(brand=" + brand + ",price=" + price + ")")
    println("Cellphone(brand=$brand,price=$price)")

変数が1つしかない場合は、2つのカッコを省略することもできます.次のコード例の最後の2つの文は完全に等価です.
    val brand = "Samsung"
    val price = 1299.99
    println("Cellphone(brand=" + brand + ",price=" + price + ")")
    println("Cellphone(brand=$brand,price=$price)")

2.8.2.関数のパラメータデフォルト値Kotlinのパラメータデフォルト機能は、サブコンストラクション関数の役割を大きく置き換えることができ、関数を定義するときに任意のパラメータにデフォルト値を設定すると、この関数を呼び出すときに呼び出し元に強制的に値を渡す必要はなく、デフォルト値を使用することができます.
fun main() {
    printParms(123)
    printParms1(str = "world")
}
//  1:             ,               。
fun printParms(num: Int, str: String = "hello") {
    println("num is $num,str is $str")
}
//  2:           ,       ,Kotlin               
fun printParms1(num: Int = 100, str: String) {
    println("num is $num,str is $str")
}

この方法は、セカンダリ構造関数の代わりに有効であり、セカンダリ構造関数の場合、より少ないパラメータを使用してStudioクラスをインスタンス化することができます.次のコードは、2つのパラメータを持つ二次構造パラメータを呼び出すパラメトリック構造関数ではありません.後者は4つのパラメータのプライマリコンストラクション関数を呼び出します.
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
    constructor(name: String, age: Int) : this("", 0, name, age) {}
    constructor() : this("", 0) {}
}

実はKotlinではまったく必要ありません.私たちは完全に1つの主構造関数でデフォルト値を設定した後、キー値を使って付与する方法で実現することができます.コードは以下の通りです.
class Student1(val sno: String="", val grade: Int=0, name: String="", age: Int=0) : Person(name, age) {
}