Kotlin Tutorial(16)介面與實作(下) by Michael | CodeData
top

Kotlin Tutorial(16)介面與實作(下)

分享:

Kotlin Tutorial(15)介面與實作(上) << 前情

各種平台與框架都會使用介面提供特定的功能,例如Android平台與JavaFX的元件監聽架構(listener),用來執行使用者操作功能的設計。這一章說明在Kotlin使用Java宣告的funtional interface與一般介面,還有使用lambda實作Java介面的作法。

在Kotlin使用Java介面

Kotlin可以使用Java API的類別與介面,下面是一個Java介面的宣告:

/* net.macdidi5.kotlin.tutorial.ch15.Printable04.java */

package net.macdidi5.kotlin.tutorial.ch15;

public interface Printable04 {
    // 兩個抽象方法宣告
    public void print();
    public void cancel();
}

Kotlin可以實作與使用Java宣告的介面,作法與使用Kotlin宣告的介面一樣:

/* net.macdidi5.kotlin.tutorial.ch15.PrintableDemo04.kt */

package net.macdidi5.kotlin.tutorial.ch15

// 宣告一個接收Printable04物件參數的函式
fun report04(printable04 : Printable04) {
    printable04.print()
    printable04.cancel()
}

// 實作Java Printable04介面
class Printable04Impl : Printable04 {
    // 覆寫print抽象方法
    override fun print() {
        println("Printable04Impl: print")
    }

    // 覆寫cancel抽象方法
    override fun cancel() {
        println("Printable04Impl: cancel")
    }
}

fun main(args: Array<String>) {

    // 建立實作Printable04介面類別的物件
    val p0401 = Printable04Impl()

    report04(p0401)

    // 使用匿名類別實作Printable04介面
    report04( object : Printable04 {
        // 覆寫print抽象方法
        override fun print() {
            println("print: I am not a functional interface.")
        }

        // 覆寫cancel抽象方法
        override fun cancel() {
            println("cancel: I am not a functional interface.")
        }
    })

}

Java 8 functional interfaces

Java 8為了支援lambda還有擴充性,在介面執行了許多改良,例如除了抽象方法外,還可以在介面宣告default與static方法。如果Java介面只有一個抽象方法(Single abstract method、SAM),可以在介面的宣告加上「@FunctionalInterface」標記。Functional interface可以在Java使用lambda expression實作,簡化應用程式的開發。

在Kotlin使用Java functional interfaces的時候,除了一般的實作方式,也可以使用lambda實作介面,跟Java lambda expression的實作類似。下面是一個Java functional interface的宣告:

/* net.macdidi5.kotlin.tutorial.ch15.Printable01.java */

package net.macdidi5.kotlin.tutorial.ch15;

// 使用FunctionalInterface標記
@FunctionalInterface
public interface Printable01 {
    // 只有一個抽象方法宣告
    public void print();
}

如果Java functional interface的抽象方法「沒有參數」也沒有「回傳值」,可以使用下面Kotlin labmda的實作語法:

Java介面名稱 {
    方法的實作
}

下面的程式碼示範在Kotlin實作Java functional interface的作法:

/* net.macdidi5.kotlin.tutorial.ch15.PrintableDemo01.kt */

package net.macdidi5.kotlin.tutorial.ch15

// 宣告一個接收Printable01物件參數的函式
fun report01(printable : Printable01) {
    printable.print()
}

// 宣告實作Printable01介面的類別
class Printable01Impl : Printable01 {
    // 覆寫Printable01介面的print抽象函式
    override fun print() {
        println("Printable01Impl implementation.")
    }
}

fun main(args: Array<String>) {

    // 建立實作Printable01介面類別的物件
    val p0101 = Printable01Impl()
    report01(p0101)

    // 宣告與實作Printable01介面的物件
    val p0102 = object : Printable01 {
        override fun print() {
            println("Printable01 implementation.")
        }
    }

    report01(p0102)

    // 如果需要實作的Java介面是functional interface
    // 就可以使用Lambda實作
    val p0103 = Printable01 {
        println("Lambda Printable01 implementation.")
    }

    report01(p0103)

    ...

}

Kotlin可以使用Java API執行需要的工作,下面是一個Java類別範例,裡面的set方法接收一個Java functional interface型態的參數:

/* net.macdidi5.kotlin.tutorial.ch15.PrintableMethod01.java */

package net.macdidi5.kotlin.tutorial.ch15;

public class PrintableMethod01 {

    private Printable01 p;

    // 接收一個Printable01型態的參數
    public void set(Printable01 p) {
        this.p = p;
    }

    public void process() {
        p.print();
    }

}

Kotlin需要傳送實作Java functional interface物件參數給方法的時候,可以使用下面示範的作法:

/* net.macdidi5.kotlin.tutorial.ch15.PrintableDemo01.kt */

package net.macdidi5.kotlin.tutorial.ch15

...

fun main(args: Array<String>) {

    ...

    val pm = PrintableMethod01()
    // 如果Java方法接收functional interface型態的參數
    // 就可以使用這樣的Lambda實作
    pm.set { println("Lambda Printable01 implementation.") }
    pm.process()

}

抽象方法的參數

下面是一個Java functional interface的宣告,抽象方法包含一個參數的宣告:

/* net.macdidi5.kotlin.tutorial.ch15.Printable02.java */

package net.macdidi5.kotlin.tutorial.ch15;

@FunctionalInterface
public interface Printable02 {
    public void print(String message);
}

如果Java functional interface的抽象方法包含「一個參數」,可是沒有「回傳值」,可以使用下面Kotlin labmda的實作語法:

Java介面名稱 {
    參數名稱 ->
    方法的實作
}

抽象方法只有一個參數的時候,Kotlin提供下面這種比較簡化的語法:

Java介面名稱 {
    方法的實作(it關鍵字表示抽象方法的參數)
}

下面是在Kotlin使用lambda實作Java介面的範例:

/* net.macdidi5.kotlin.tutorial.ch15.PrintableDemo02.kt */

package net.macdidi5.kotlin.tutorial.ch15

// 宣告一個接收Printable02物件參數的函式
fun report02(printable02 : Printable02) {
    printable02.print("Message02.")
}

fun main(args: Array<String>) {

    // 宣告與實作Printable02介面的物件
    val p0201 = object : Printable02 {
        // 覆寫Printable02介面的print抽象函式
        override fun print(message: String) {
            println("Printable02 implementation: $message")
        }
    }

    report02(p0201)

    // 使用Lambda實作
    val p0202 = Printable02 {
        // 因為print抽象函式只有一個參數
        // 在lambda裡面使用「it」代表參數
        println("Lambda Printable02 implementation: $it")
    }

    report02(p0202)

    // 在方法的參數使用Lambda實作Printable02介面的物件
    report02(Printable02 {
        // 「it」是print抽象函式的參數
        println("Lambda Printable02 implementation: $it")
    })

}

下面是一個Java functional interface,它的抽象方法宣告了多個參數:

/* net.macdidi5.kotlin.tutorial.ch15.Printable03.java */

package net.macdidi5.kotlin.tutorial.ch15;

@FunctionalInterface
public interface Printable03 {
    public String getMessage(String prefix, String message);
}

如果Java functional interface的抽象方法包含「多個參數」,而且有「回傳值」,可以使用下面Kotlin labmda的實作語法:

Java介面名稱 {
    參數名稱, ... ->
    方法的實作
    回傳值
}

下面是在Kotlin使用lambda實作Java介面的範例:

/* net.macdidi5.kotlin.tutorial.ch15.PrintableDemo03.kt */

package net.macdidi5.kotlin.tutorial.ch15

// 宣告一個接收Printable03物件參數的函式
fun report03(printable03 : Printable03) {
    println(printable03.getMessage("Prefix", "Message03"))
}

fun main(args: Array<String>) {

    // 宣告與實作Printable03介面的物件
    val p0301 = object : Printable03 {
        override fun getMessage(prefix : String?, 
                                message : String?) : String {
            return "Printable03 implementation: $prefix-$message"
        }
    }

    report03(p0301)

    // 使用Lambda實作
    val p0302 = Printable03 {
        // 因為方法有多個參數宣告,所以必須在「->」前面定義所有參數名稱
        // 在「->」後面才是方法的實作
        prefix, message ->
        // 最後一行敘述是回傳值
        "Lambda Printable03 implementation: $prefix-$message"
    }

    report03(p0302)

    val p0303 = Printable03 {
        // 不需要使用的參數可以使用底線代替
        _, message ->
        "Lambda Printable03 implementation: $message"
    }

    report03(p0303)

}

Kotlin使用Java介面範例

Java程式語言在Android與JavaFX技術,經常使用介面執行使用者操作功能的設計,Kotlin在設計這類應用程式的時候,就需要實作與使用Java介面。下面是一個模擬使用者按鍵操作的Java事件類別:

/* net.macdidi5.kotlin.tutorial.ch15.KeyEvent.java */

package net.macdidi5.kotlin.tutorial.ch15;

public class KeyEvent {

    // Java enum型態,按鍵的操作種類
    public enum Action {
        KEY_DOWN, KEY_UP
    }

    private Action action;

    public KeyEvent(Action action) {
        this.action = action;
    }

    public Action getAction() {
        return action;
    }

}

下面是一個模擬畫面元件的Java類別:

/* net.macdidi5.kotlin.tutorial.ch15.View.java */

package net.macdidi5.kotlin.tutorial.ch15;

public class View {

    private OnKeyListener listener;

    // 按鍵監聽介面
    @FunctionalInterface
    public interface OnKeyListener {
        boolean onKey(View v, int keyCode, KeyEvent event);
    }

    // 設定按鍵監聽介面物件
    public void setOnKeyListener (View.OnKeyListener listener) {
        this.listener = listener;
    }

    public String toString() {
        return "View";
    }

    // 按下按鍵
    public void keyDown() {
        listener.onKey(this, 32, new KeyEvent(KeyEvent.Action.KEY_DOWN));
    }

    // 放開按鍵
    public void keyUp() {
        listener.onKey(this, 67, new KeyEvent(KeyEvent.Action.KEY_UP));
    }

}

下面是Kotlin設計使用者操作按鍵後執行工作的範例:

/* net.macdidi5.kotlin.tutorial.ch15.OnKeyDemo01.kt */

package net.macdidi5.kotlin.tutorial.ch15

// 宣告實作View.OnKeyListener介面的類別
class OnKeyListenerImpl : View.OnKeyListener {
    override fun onKey(view: View,
                       keyCode: Int,
                       event: KeyEvent): Boolean {
        println("$view : $keyCode : ${event.action}")
        return true
    }
}

fun main(args: Array<String>) {

    // 建立View物件
    val view01  = View()
    // 設定按鍵監聽物件
    view01.setOnKeyListener(OnKeyListenerImpl())
    // 按下按鍵
    view01.keyDown()

    val view02 = View()

    // 使用lambda實作View.OnKeyListener介面
    view02.setOnKeyListener({
        view, keyCode, event ->
        println("$view : $keyCode : ${event?.action}")
        true
    })

    // 放開按鍵
    view02.keyUp()

    val view03 = View()

    // 使用lambda實作View.OnKeyListener介面
    view03.setOnKeyListener({
        // 不需要使用的參數使用底線(_)代替
        _, _, event ->
        println("Action: ${event.action}")
        true
    })

    // 放開按鍵
    view03.keyUp()

}

下一步

完成介面的設計與實作以後,接下來準備認識Kotlin提供的資料類別與使用存取修飾子執行封裝的作法。

相關的檔案都可以在GitHub瀏覽與下載。

https://github.com/macdidi5/Kotlin-Tutorial

後續 >> Kotlin Tutorial(17)存取修飾子與資料類別

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

Xiangwei Chiou01/31

看完這篇終於搞懂下面程式碼的來龍去脈
https://developer.android.com/kotlin/get-started.html