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的實作語法:
下面的程式碼示範在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瀏覽與下載。
http://github.com/macdidi5/Kotlin-Tutorial
後續 >> Kotlin Tutorial(17)存取修飾子與資料類別
|
Xiangwei Chiou
01/31
看完這篇終於搞懂下面程式碼的來龍去脈
http://developer.android.com/kotlin/get-started.html