[Kotlin]高度な文法整理


リファレンス映像


https://www.inflearn.com/course/コトリン-要約-教室/dashboard

1.Lambda Expression


ラムダ式はvalueのように処理可能な匿名関数である.
1)メソッドのパラメータ.
2)戻り値として使用できます.
ランダの基本定義
val lambdaName : Type = { argumentList -> codeBody }
val lambdaName = { argumentName : Type -> codeBody }

画像ソース:https://youtu.be/z8prdZgk4kA?t=317
//val square : (Int) -> (Int) = {number -> number*number}
val square = {number: Int -> number*number}

val nameAge = {name: String, age: Int ->
    // 표현식이 여러 줄인 경우, 마지막 줄을 리턴함.
    "My name is $name I'm $age"
}

fun main(){
    println(square(12))
    println(nameAge("Haeun", 22))
}
144
My name is Haeun I'm 22

1-2. 拡張関数


拡張関数は、クラスに関数を追加する匿名関数です.たとえば、Stringクラスを拡張する匿名関数を作成します.
fun main(){
    val a = "haeun said"
    val b = "mike said"
    println(a.pizzaIsGreat())
    println(b.pizzaIsGreat())
}

val pizzaIsGreat : String.() -> String = {
    // this는 이 확장함수가 불러올 객체 (여기서는 String)
    "$this : pizza is the best!"
}
haeun said : pizza is the best!
mike said : pizza is the best!
拡張関数のデフォルトフォーマット
valamマルチネーム:クラス名.(入力タイプ)->出力タイプ={関数の内容と結果を返す}
cf)コトリンで複数の値を返すことができるかどうか知りたいので調べてみましたが、ランダムに凡例を作成することはできず、PairやTripleクラス、配列、リストを使うことができます.もっと一般的なのは、データクラスを使用することです.
https://stackoverflow.com/questions/47307782/how-to-return-multiple-values-from-a-function-in-kotlin-like-we-do-in-swift

1-3. 蓝达の返事

fun main() {
    val a = "haeun said"
    val b = "mike said"
    // 확장함수의 매개변수가 없는 경우
    println(a.pizzaIsGreat())
    println(b.pizzaIsGreat())
    
    // 확장함수의 매개변수가 있는 경우
    println(extendString("haeun", 22))
    println(calculateGrade(100))
}

// String 클래스를 확장하는 익명함수
val pizzaIsGreat : String.() -> String = { // 매개변수가 없는 경우
    // this는 확장함수가 불러올 String 객체 자체
    "$this : pizza is the best!"
}

fun extendString(name: String, age: Int) : String {
    // 매개변수가 1개인 경우 it를 사용한다.
    val introduceMyself: String.(Int) -> String = {
        // this는 확장함수가 불러올 String 객체 자체
        "I am $this and $it years olds"
    }
    return name.introduceMyself(age)
}

// 람다의 리턴
val calculateGrade : (Int) -> String = {
    when(it){ // when이 expression으로 쓰일 때는 else문 필수
        in 0..40 -> "fail"
        in 41..70 -> "pass"
        in 71..100 -> "perfect"
        else -> "Error"
    }
}
haeun said : pizza is the best!
mike said : pizza is the best!
I am haeun and 22 years olds
perfect

1-4. ランダを表す2つの方法

fun main() {
    // 1. 람다식 정의 후 사용하기
    val lambda = { number: Double ->
        number == 4.3213
    }
    println(invokeLambda(lambda)) // 5.2343 == 4.3213 ? false

    // 2. 람다 리터럴 (중괄호를 바로 사용하는 경우)
    println(invokeLambda({ it > 3.22 })) // 5.2343 > 3.22 ? true
}

// 람다를 표현하는 2가지 방법
// 람다식은 마치 'value처럼' 사용할 수 있는 익명함수여서
// 함수의 매개변수 또는 리턴값이 될 수 있다.
fun invokeLambda(lambda: (Double) -> Boolean) : Boolean {
    return lambda(5.2343)
}

1-5. イベントを処理するリスナーでramda式を使用する


xmlで使用されるすべてのビューをメモリにオブジェクト化するには、既存のjavaはfindViewByIdメソッドを使用して拡大する必要があります.これは、次のコードのようにアプリケーション単位で構築するのが面倒です.gradleファイルに「cortline android expensionプラグイン」を適用する場合は、この手順を省略できます.
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions' // 익스텐션 플러그인 적용
}
以下のコードに示すようにsetOnClickListenerでラムダ式を使用するとコード長を大幅に短縮できます.
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?){
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener(object: View.OnClickListener{
            override fun onClick(p0: View?) {
                // to do...
            }
        })

        // View.OnClickListener처럼
        // 코틀린 인터페이스가 아닌 자바 인터페이스이고,
        // 인터페이스의 메소드가 하나만 있는 경우, 람다식으로 쓸 수 있다.
        button.setOnClickListener { 
            // to do...
        }
        
    }
}
// View.class 파일을 열어보면, OnClickListener 인터페이스에는 딱 하나의 메소드만 있다.
public interface OnClickListener {
        void onClick(View var1);
    }

2.データクラス


コトリンでは、データコンテナになるクラスを個別に作成できます.JAvaはPOJOクラスで「空の棚の役割」クラスを作成しましたが、問題は似たようなコードが何度も繰り返され、ボイラーボードコードが生成されたことです.しかし,コトリンのデータクラスを用いることでこれらの問題を解決できる.
https://charlezz.medium.com/ボイラプレート-コードバー-テンプレート-コード-8309 a 8 d 3297
たとえば、航空券情報を含むTicketデータクラスを作成します.
data class Ticket(
    val companyName: String,
    val name: String,
    var date: String,
    var seatNumber: Int
)
データクラスは、上記のプライマリジェネレータを宣言するだけで、toString()、hashCode()、equals()およびcopy()メソッドを自動的に生成できます.
data class Ticket(
    val companyName: String,
    val name: String,
    var date: String,
    var seatNumber: Int
)

class TicketNormal(
    val companyName: String,
    val name: String,
    var date: String,
    var seatNumber: Int
)

fun main() {
    val ticketA = Ticket("koreaAir", "haeunLee", "2022-01-03", 14)
    val ticketB = TicketNormal("koreaAir", "haeunLee", "2022-01-03", 14)
    println(ticketA)
    println(ticketB) // ticketB 객체가 할당된 메모리 주소 출력
}
Ticket(companyName=koreaAir, name=haeunLee, date=2022-01-03, seatNumber=14)
com.tutorial.kotlinbasic.TicketNormal@2d6e8792
通常のクラスのオブジェクトはメモリアドレスを出力し、コトリンのデータクラスはオブジェクトに含まれる情報を表示します.

3. Companion Object


https://wikidocs.net/228
https://www.bsidesoft.com/8187

JAvaはいつstaticを使用しますか?


すべてのオブジェクトに同じ値がある場合は、メモリを再割り当てするたびに静的に設定する必要はありません.静的はその名の通り、割り当てられたメモリは「静的」であり、すべてのオブジェクトが共有できます.静的メンバーは、オブジェクトを作成せずに「クラス名」メソッドを直接呼び出すことができるため、クラスメンバーとも呼ばれます.

Companion object


伴生対象はその名の通り類と伴う対象である.Javaのstaticと似ているクラス名を直接呼び出すことができますが、仲間オブジェクトはあくまでオブジェクトであり、staticとは異なります.
class MyClass{
    companion object{
        val prop = "나는 Companion object의 속성이다."
        fun method() = "나는 Companion object의 메소드다."
    }
}
fun main() {
    // companion object의 멤버에 접근할 때,
    // Companion 키워드를 생략하고 클래스명으로 바로 접근할 수 있다!
    println(MyClass.Companion.prop)
    println(MyClass.Companion.method())
    println(MyClass.prop)
    println(MyClass.method())

    val comp1 = MyClass.Companion // --(1)
    println(comp1.prop)
    println(comp1.method())

    // 명시적으로 객체를 생성할 때도 Companion 키워드 생략 가능
    val comp2 = MyClass // --(2)
    println(comp2.prop)
    println(comp2.method())
}
上記のコードのコメント(1)に示すように、変数には仲間オブジェクトを割り当てることができますが、Javaの静的メンバーを変数に割り当てることはできません.
また,注釈(2)では,クラスで定義された同伴オブジェクトがクラス名のみで参照アクセスできることを覚えておく必要がある.
ここから、staticキーのみを使用してクラスメンバーを仲間オブジェクトのように独立したオブジェクトと見なすことはできません.

なぜCompanion objectが必要なのですか?

class Book private constructor(val id: Int, val name: String){
    companion object {
        fun create() = Book(0, "animal farm")
    }
}

fun main() {
    //val book = Book() // Cannot access '<init>': it is private in 'Book'
    val book = Book.Companion.create()
    println("${book.id} ${book.name}")
}
上記のコードに示すように、private作成者は外部でオブジェクトを作成できませんが、クラスのメンバーにアクセスする場合は、パートナーオブジェクトを使用できます.
class Book private constructor(val id: Int, val name: String){
    companion object BookFactory : IdProvider{
        override fun getId(): Int {
            return 100
        }
        val myBook = "new book"
        fun create() = Book(getId(), myBook)
    }
}

interface IdProvider {
    fun getId() : Int
}

fun main() {
    val book = Book.create()
    println("${book.id} ${book.name}")

    // companion object가 오버라이딩 한 메소드 역시 클래스명으로 접근 가능하다.
    val bookId = Book.getId()
    println(bookId)
}

4. Object


コトリンにはJavaにない独自の宣言方法(singleton;インスタンスのクラスが1つしかない)があります.classキーワードではなくobjectキーワードを以下のように使用できます.
// Singleton Pattern: 인스턴스가 오직 하나만 생성되어야 하는 경우 사용하는 패턴
// CarFactory 객체는 모든 앱을 실행할 때 딱 한번만 만들어진다. (불필요한 메모리 사용 방지)
object CarFactory {
    val cars = mutableListOf<Car>()
    fun makeCar(horsePower: Int) : Car {
        val car = Car(horsePower)
        cars.add(car)
        return car
    }
}

data class Car(val horsePower: Int)

fun main() {
    // CarFactory 인스턴스는 컴파일할 때 한번만 만들어서 계속 재사용
    val car = CarFactory.makeCar(10)
    val car2 = CarFactory.makeCar(200)
    println(car)
    println(car2)
    println(CarFactory.cars.size.toString())
}
Car(horsePower=10)
Car(horsePower=200)
2
objectは、var obj = object: MyClass(){}またはvar obj = object: MyInterface{}のように特定のクラスまたはインタフェースを拡張することによって作成され、宣言ではなく式(var obj = object{})を生成することができる.
モノトーンなので、objectメソッドを定義してシステム全体で使用する機能を実行するのに役立つかもしれませんが、グローバル状態を維持する際にスレッド競合などの問題が発生する可能性があります.
cf)競合:あるスレッドが別のスレッドが取得したロックが解放されるのを待つ
objectは、言語レベルで安全な単輪を作成できるため、非常に役立ちます.