ฟังก์ชันขอบเขตใน Kotlin คือฟังก์ชันไลบรารีมาตรฐานห้าตัว (let, run, with, apply, also) ที่ดําเนินการบล็อกโค้ดในบริบทของอ็อบเจกต์ ฟังก์ชันเหล่านี้แตกต่างกันที่วิธีการเข้าถึงอ็อบเจกต์ (ผ่าน it หรือ this) และค่าที่ส่งคืน (อ็อบเจกต์บริบทหรือผลลัพธ์ของแลมบ์ดา) ตามเอกสาร Kotlin (2026) การเลือกฟังก์ชันขอบเขตที่ถูกต้องจะช่วยลดโค้ดเทมเพลตได้ 25–40% ฟังก์ชันขอบเขต ช่วยเขียนการเริ่มต้น การกําหนดค่า และการแปลงอ็อบเจกต์ในรูปแบบdeclarative
ประเด็นสําคัญ
ฟังก์ชันขอบเขต คือชุดของฟังก์ชันห้าตัว (let, run, with, apply, also) ที่สร้างไว้ในไลบรารีมาตรฐานของ Kotlin แต่ละฟังก์ชันรับแลมบ์ดาและเปลี่ยนขอบเขตชั่วคราวเพื่อให้โค้ดภายในแลมบ์ดาทํางานในบริบทของอ็อบเจกต์เฉพาะ ซึ่งช่วยลดการซ้ําชื่ออ็อบเจกต์สําหรับการเรียกหลายครั้งและจัดกลุ่มการดําเนินการที่เกี่ยวข้องไว้ในบล็อกเดียว
ลักษณะสําคัญสองประการที่แยกแยะฟังก์ชันขอบเขตคือ วิธีการเข้าถึงอ็อบเจกต์บริบท (ผ่าน this หรือผ่าน it) และค่าที่ส่งคืน (อ็อบเจกต์บริบทเองหรือผลลัพธ์แลมบ์ดา) การเข้าถึงผ่าน this ทําให้อ็อบเจกต์เป็นตัวรับโดยนัย — เมธอดและคุณสมบัติถูกเรียกโดยไม่มีคํานําหน้า การเข้าถึงผ่าน it ส่งอ็อบเจกต์เป็นพารามิเตอร์ที่ชัดเจนของแลมบ์ดา การส่งคืนอ็อบเจกต์ช่วยให้ chain ได้ การส่งคืนผลลัพธ์ช่วยให้กําหนดค่าที่แปลงแล้ว
ในอดีต ฟังก์ชันขอบเขตเกิดขึ้นเพื่อแทนที่รูปแบบ Builder แบบคลาสสิกและตัวแปรชั่วคราวสําหรับการเริ่มต้น แทนที่จะสร้างคลาส builder แยกต่างหาก นักพัฒนาเขียน apply { คุณสมบัติ1 = ...; คุณสมบัติ2 = ... } ซึ่งช่วยลดจํานวนโค้ดและทําให้การเริ่มต้นอ่านง่ายขึ้น ในระบบนิเวศของ Kotlin ฟังก์ชันขอบเขตถูกใช้ในเฟรมเวิร์กและไลบรารีทั้งหมดเป็นสํานวนภาษามาตรฐาน
การเลือกฟังก์ชันขอบเขตที่ถูกต้องถูกกําหนดโดยคําถามสองข้อ: คุณต้องการใช้ this หรือ it เพื่อเข้าถึงอ็อบเจกต์ และคุณต้องการส่งคืนอ็อบเจกต์เองหรือผลลัพธ์แลมบ์ดา การรวมกันของลักษณะไบนารีสองอย่างให้สี่ตัวเลือกที่เป็นไปได้ และ with ครอบครองตําแหน่งพิเศษในฐานะฟังก์ชันเดียวที่ไม่ใช่ส่วนขยาย
| ฟังก์ชัน | การเข้าถึง | การส่งคืน | ส่วนขยาย? | สถานการณ์ทั่วไป |
|---|---|---|---|---|
| let | it | ผลลัพธ์แลมบ์ดา | ใช่ | การแปลง, การตรวจสอบ null |
| run | this | ผลลัพธ์แลมบ์ดา | ใช่ | การคํานวณกับบริบท |
| with | this | ผลลัพธ์แลมบ์ดา | ไม่ | การจัดกลุ่มการเรียก |
| apply | this | อ็อบเจกต์บริบท | ใช่ | การเริ่มต้นคุณสมบัติ |
| also | it | อ็อบเจกต์บริบท | ใช่ | ผลข้างเคียง, การบันทึก |
เพื่อกําหนดค่าอ็อบเจกต์โดยไม่ส่งคืน ค่าใหม่ ให้ใช้ apply (การเข้าถึงผ่าน this) หรือ also (การเข้าถึงผ่าน it) เพื่อคํานวณค่าใหม่ตามอ็อบเจกต์ ให้ใช้ let (การเข้าถึงผ่าน it) หรือ run (การเข้าถึงผ่าน this) เพื่อจัดกลุ่มการเรียกโดยไม่ต้อง chain ให้ใช้ with การปฏิบัติตามแผนนี้ทําให้โค้ดคาดเดาได้สําหรับนักพัฒนาคนอื่นที่คุ้นเคยกับ Kotlin
apply คือฟังก์ชันขอบเขตที่ส่งคืนอ็อบเจกต์บริบทและให้การเข้าถึงผ่าน this เป็นเครื่องมือที่เหมาะสําหรับการเริ่มต้นคุณสมบัติของอ็อบเจกต์หลังการสร้าง ภายในบล็อก apply สามารถตั้งค่าคุณสมบัติได้โดยไม่ต้องซ้ําชื่ออ็อบเจกต์ เมธอดส่งคืนอ็อบเจกต์เอง ทําให้สามารถฝัง apply ใน chains หรือใช้ใน initializers
data class ServerConfig(
var host: String = "localhost",
var port: Int = 8080,
var useTls: Boolean = false
)
val config = ServerConfig().apply {
host = "api.example.com"
port = 443
useTls = true
}
ในตัวอย่าง apply กําหนดค่า ServerConfig โดยเข้าถึงคุณสมบัติโดยตรงผ่าน this (this ถูกละไว้) หากไม่มี apply จะต้องเขียน config.host = ..., config.port = ... — การอ้างอิงซ้ําสามครั้งไปยังตัวแปรเดียวกัน การส่งคืนอ็อบเจกต์ช่วยให้บันทึกผลลัพธ์การกําหนดค่าใน val config also แตกต่างจาก apply ส่งอ็อบเจกต์ผ่าน it และเหมาะสําหรับผลข้างเคียง
fun saveUser(user: User) {
validate(user).also { result ->
println("ผลการตรวจสอบ: $result")
}
val savedUser = user.also {
log("Saving user ${it.id}")
database.save(it)
}
}
ในตัวอย่าง also ใช้สําหรับการบันทึก — ขณะส่งคืนอ็อบเจกต์ดั้งเดิม จะดําเนินการผลข้างเคียง (แสดงข้อความ) โดยไม่แก้ไขอ็อบเจกต์ นี่คือ chain: validate(user) ส่งคืน User, also บันทึกผลลัพธ์และส่งต่อผู้ใช้ also ยังมีประโยชน์สําหรับการเพิ่มองค์ประกอบในคอลเลกชันหรือการดีบักระหว่าง chain การแปลง
let คือฟังก์ชันขอบเขตที่ส่งคืนผลลัพธ์แลมบ์ดาและส่งบริบทผ่าน it เป็นฟังก์ชันหลักสําหรับการแปลงที่ปลอดภัยกับ null: ถ้าอ็อบเจกต์เป็น nullable การรวมกัน ?.let { } จะดําเนินการบล็อกสําหรับค่าที่ไม่ใช่ null เท่านั้น let ยังใช้เพื่อจํากัดขอบเขตของตัวแปรชั่วคราวและการแปลงแบบ map จากชนิดหนึ่งไปยังอีกชนิดหนึ่ง
fun findUser(id: Int): User?
val displayName = findUser(42)?.let { user ->
"${user.name} (${user.email})"
} ?: "Unknown user"
// let พร้อมการแปลงคอลเลกชัน
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
it.toIntOrNull()
} ?: 0
ในตัวอย่างแรก let ถูกเรียกผ่าน ?. — บล็อกจะดําเนินการก็ต่อเมื่อ findUser ส่งคืนค่าที่ไม่ใช่ null แลมบ์ดารับ user ผ่าน it (สามารถเปลี่ยนชื่อเพื่อความชัดเจน) และส่งคืนสตริงที่จัดรูปแบบแล้ว หากไม่พบผู้ใช้ โอเปอเรเตอร์ Elvis ?: จะให้ค่าเริ่มต้น ในตัวอย่างที่สอง let แปลงสตริงเป็นตัวเลขโดยมีโอกาสเป็น null
run คืออะนาล็อกของ let ที่มีการเข้าถึงผ่าน this ส่งคืนผลลัพธ์แลมบ์ดา run สะดวกสําหรับการคํานวณที่ต้องการบริบทอ็อบเจกต์แต่ส่งคืนค่าใหม่ การเรียก run() โดยไม่มีอ็อบเจกต์ (เช่น run { ... }) สร้างขอบเขตชั่วคราวสําหรับตัวแปร with คือไม่ใช่ส่วนขยายที่มีการเข้าถึงผ่าน this เหมาะสําหรับการจัดกลุ่มการเรียกโดยไม่ต้อง chain
// run เป็นการคํานวณตามบริบท
val transformed = listOf(1, 2, 3).run {
filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]
// with สําหรับจัดกลุ่มการเรียก
val info = with(StringBuilder()) {
append("Name: ")
append("Alice")
toString()
}
ในตัวอย่าง run บนลิสต์ส่งคืนผลลัพธ์ของการกรองและการแมป — การแปลงเอง ไม่ใช่ลิสต์ with รับ StringBuilder เป็นอาร์กิวเมนต์ (ไม่ผ่านจุด) และทํางานภายในกับ this: append, append, toString ผลลัพธ์ของ with คือสตริง info การเลือกระหว่าง run และ with มักเป็นอัตนัย แต่ with เป็นที่นิยมเมื่ออ็อบเจกต์ถูกส่งมาจากที่อื่นโดยไม่ต้อง chain
with แตกต่างจากฟังก์ชันขอบเขตอื่นตรงที่ไม่ใช่ฟังก์ชันส่วนขยาย มันรับอ็อบเจกต์เป็นอาร์กิวเมนต์แรกและแลมบ์ดาเป็นอาร์กิวเมนต์ที่สอง ภายในแลมบ์ดา อ็อบเจกต์สามารถเข้าถึงได้ผ่าน this with สะดวกสําหรับการจัดกลุ่มการเรียกเมธอดอ็อบเจกต์โดยไม่บันทึกผลลัพธ์หรือเมื่อคุณต้องการ "เข้า" สู่บริบทชั่วคราวเพื่ออ่านหลายคุณสมบัติ
val user = User("Alice", "alice@example.com", "Admin")
val summary = with(user) {
"User $name has role $role and email $email"
}
println(summary)
// with สําหรับการเริ่มต้นคอมโพเนนต์ UI
with(TextView(context)) {
text = "Hello"
textSize = 18.0f
setTextColor(Color.BLUE)
}
ในตัวอย่างแรก with สร้างสตริง summary โดยเข้าถึงคุณสมบัติ user โดยตรงผ่าน this หากไม่มี with จะต้องเขียน "User ${user.name} has role ${user.role}" — การซ้ําคําว่า user ในตัวอย่างที่สอง with จัดกลุ่มการกําหนดค่า TextView: เมธอดและคุณสมบัติทั้งหมดถูกเรียกบนอ็อบเจกต์เดียวโดยไม่ซ้ํา ข้อเสียของ with — ไม่สามารถใช้ใน chain ได้เนื่องจากไม่ใช่ส่วนขยายและไม่ส่งคืนอ็อบเจกต์บริบท
คําถามที่พบบ่อย
ใช้ apply — มันส่งคืนอ็อบเจกต์และให้การเข้าถึงผ่าน this ทําให้สามารถตั้งค่าคุณสมบัติโดยไม่ต้องซ้ําชื่ออ็อบเจกต์ also เหมาะสมหากคุณต้องการพารามิเตอร์ it ที่ชัดเจนเพื่อแยกความแตกต่างจากคุณสมบัติของอ็อบเจกต์
let ส่งบริบทผ่าน it (พารามิเตอร์ที่ชัดเจน), run — ผ่าน this (ตัวรับโดยนัย) let สะดวกสําหรับการตรวจสอบ null และการแปลง run — สําหรับการคํานวณกับบริบท ทั้งสองส่งคืนผลลัพธ์แลมบ์ดา
with ไม่ใช่ส่วนขยาย — มันรับอ็อบเจกต์เป็นอาร์กิวเมนต์ ใช้ with เพื่อจัดกลุ่มการเรียกเมื่อไม่ต้องการ chain apply เป็นส่วนขยายที่ส่งคืนอ็อบเจกต์ สะดวกใน initializers และรูปแบบ builder
ได้ แต่ระวัง this — this ที่ซ้อนกันทําให้เกิดความสับสน ใช้ let หรือ also ที่มีชื่อชัดเจนสําหรับบริบทที่ซ้อนกันเพื่อหลีกเลี่ยงความกํากวมเมื่ออ้างถึงอ็อบเจกต์ภายนอก
ฟังก์ชันขอบเขตทั้งหมดเป็น inline ดังนั้นจึงไม่สร้างโอเวอร์เฮดจากคลาสที่ไม่ระบุชื่อ ความแตกต่างด้านประสิทธิภาพระหว่างฟังก์ชันเหล่านั้นเล็กน้อย การเลือกควรขึ้นอยู่กับความสามารถในการอ่านและธรรมเนียมปฏิบัติ ไม่ใช่ความเร็ว
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม