Extension Functions ง่ายๆ สไตล์ Kotlin
พอกันทีกับ static utility method
ผมเชื่อว่าหลายคนคงเคยเจอสถานการณ์แบบนี้ใน Java มาก่อน
สถานการณ์ที่คุณต้องทำงานกับ type ที่คุณไม่ได้เป็นเจ้าของ แต่คุณรู้สึกมันใช้ยากเหลือเกิน
Date today = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String todayString = sdf.format(today);
จนคุณต้องสร้าง static utility method ขึ้นมาช่วยเหลือ
// call site
Date today = new Date();
String todayString = DateUtil.format(today);// static utility method
class DateUtil {
public static String format(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
}
โอเค ดีขึ้น เหลือแค่ 1 method call
แต่คุณรู้สึกว่าทำไมเราต้องมาเรียก method จาก utility class ด้วย
คุณเลยตัดสินใจ extend Date ซะเลย
class FormatableDate extends Date {
public String format() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(this);
} //...
}
ตอนใช้ก็สะดวกแค่นี้
FormatableDate today = new FormatableDate();
String todayString = today.format();
แล้วคุณก็ตระหนักได้ว่าคุณต้องใช้ FormatableDate แทน Date ทุกที่เลย
Extension Functions
Extension function ใน Kotlin อนุญาตให้คุณเติม function เพิ่มบน type ใดๆก็ได้
//DateExtensions.ktDate.format(): String {
val sdf = SimpleDateFormat("yyyy-MM-dd")
return sdf.format(this)
}
การเติม extension function ไม่จำเป็นต้องใส่ไว้ใน class ใดๆ คุณประกาศลอยๆแบบนี้ในไฟล์ kt ได้เลย (เค้าเรียกว่า top level)
Date.format(): String
เป็นการเติมฟังก์ชั่น format
บน Date
ในบริบทนี้ Date
มีศัพท์เทคนิคเรียกว่า receiver type หมายถึงไทป์ที่เรากำลังประกาศฟังก์ชั่นเติมอยู่นั่นเอง
this
ใน return sdf.format(this)
มีศัพท์เทคนิคเรียกว่า receiver object หรือออบเจ็คที่เราเรียกฟังก์ชั่นนั่นเอง
val today = Date()
val todayString = today.format()
ทีนี้คุณก็เรียก format()
ได้บน Date
ทุกตัวแล้ว ไม่ต้องไป subclass ใช้คลาสลูกให้ยุ่งยาก ไม่ต้องไปทำ static utility method ให้เวิ่นเว้อ
ไม่เพียงแค่นั้น คุณยังสามารถเรียกใช้ในฝั่ง Java ได้ด้วย
Date today = new Date();
String dateString = DateExtensionsKt.format(today);
จะเห็นว่าเหมือน DateUtil อันเดิมของเราเลย เพราะภายใต้ syntax อันเรียบง่ายงั้น มันก็ทำงานเป็น static method ธรรมดาอันนึงนั่นแหละ
Extension Properties
นอกจาก extension function แล้ว Kotlin ยังมี extension property อีกต่างหาก ยกตัวอย่างเช่น บ่อยครั้งที่เราทำงานกับ List แล้วเราต้องการ index สุดท้าย เราก็ต้องมานั่ง list.size — 1
เสมอ เราแค่สร้าง extension property ดังนี้
val <T> List<T>.lastIndex: Int
get() = size - 1
ตอนใช้ก็แค่
val strings = listOf("one", "two")
println("The last index is ${strings.lastIndex}")
และแน่นอนว่าใช้ใน Java ได้เหมือนกัน
List<String> strings = //...
int lastIndex = CollectionsKt.getLastIndex(strings);
อันที่จริงคุณไม่ต้องสร้าง lastIndex
เองหรอกนะ เพราะใน standard library เค้ามีไว้ให้แล้ว
สรุป
Extensions functions/properties เป็นฟีเจอร์ที่ดูเหมือนจะเกิดขึ้นมาอำนวยความสะดวกเฉยๆ แต่เมื่อเรานำไปใช้ผสมผสานกับฟีเจอร์อื่นๆแล้วจะทรงพลังกว่านี้อีก
ทุกอย่างสามารถอธิบายได้ ใช้กับ Java ได้ มีกฎชัดเจน ไม่มี magic แต่อย่างใด
เอ๊ะ พูดถึง magic เราไปร่วมสำรวจ extension functions ในโลกมหัศจรรย์ของ Kotlin กันต่อดีกว่าครับ