Extension Functions ง่ายๆ สไตล์ Kotlin

พอกันทีกับ static utility method

Travis P
Black Lens
Published in
2 min readMay 23, 2017

--

ผมเชื่อว่าหลายคนคงเคยเจอสถานการณ์แบบนี้ใน 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 กันต่อดีกว่าครับ

--

--

Travis P
Black Lens

Android Developer, Kotlin & Flutter Enthusiast and Gamer