Companion object ใน Kotlin: คืออะไร ไวยากรณ์ และการใช้งาน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-21 เวลาอ่าน: 9 นาที

companion object เป็นกลไกของภาษา Kotlin ที่แทนที่คีย์เวิร์ด static ของ Java สำหรับการประกาศสมาชิกแบบสแตติกของคลาส Kotlin ไม่มี static ในตัว — แต่จะใช้การประกาศ object พร้อมกับตัวปรับแต่ง companion ภายในคลาส ตามข้อมูลจาก JetBrains, 2025 companion object อนุญาตให้เรียกเมธอดและคุณสมบัติผ่านชื่อคลาส ทำให้เทียบเท่ากับสมาชิกแบบสแตติกในระบบนิเวศ Java อย่างสมบูรณ์

ประเด็นสำคัญ

  • companion object คือการประกาศ object ภายในคลาสด้วยคีย์เวิร์ด companion แทนที่ Java static
  • สมาชิกแบบสแตติก สามารถเข้าถึงได้ผ่านชื่อคลาสโดยไม่ต้องสร้างอินสแตนซ์: ClassName.method()
  • companion object ที่มีชื่อ อนุญาตให้ตั้งชื่อให้กับออบเจกต์เพื่อให้โค้ดอ่านง่ายขึ้น
  • อินเทอร์เฟซ ใน Kotlin ก็สามารถมี companion object พร้อมการ implement เมธอดได้
  • @JvmStatic — คำอธิบายประกอบสำหรับส่งออกเมธودของ companion object เป็นเมธอด static จริงใน Java

Companion object ใน Kotlin คืออะไร?

companion object เป็นประเภทพิเศษของ การประกาศ object ใน Kotlin ที่ถูกทำเครื่องหมายด้วยคีย์เวิร์ด companion ซึ่งอนุญาตให้ประกาศสมาชิกที่เป็นของคลาสแทนที่จะเป็นอินสแตนซ์ — นั่นคือสมาชิกแบบสแตติกในศัพท์ของ Java ต่างจาก Java ที่ static เป็นตัวปรับแต่งสำหรับฟิลด์และเมธอดแต่ละรายการ ใน Kotlin สมาชิกแบบสแตติกจะถูกจัดกลุ่มภายในออบเจกต์คู่หูเดียว

คลาสใดๆ ใน Kotlin สามารถมี companion object ได้หนึ่งตัวพอดี สมาชิกของออบเจกต์นี้สามารถเข้าถึงได้ผ่านชื่อคลาสโดยไม่ต้องสร้างอินสแตนซ์: MyClass.method() ในแง่ของไบต์โค้ด companion object จะถูกคอมไพล์เป็นคลาสภายในแยกต่างหาก และเมธอดของมันจะกลายเป็นเมธอดสแตติกของคลาสภายนอกเมื่อใช้คำอธิบายประกอบ @JvmStatic

ตามข้อมูลจาก Google I/O 2024 companion object ได้กลายเป็นรูปแบบหลักสำหรับเมธอดโรงงาน ค่าคงที่ และฟังก์ชันอำนวยความสะดวกในแอปพลิเคชัน Android สมัยใหม่ที่เขียนด้วย Kotlin นักพัฒนาเลือกใช้แทนคลาสอำนวยความสะดวกที่มีฟังก์ชันระดับแพ็คเกจ เนื่องจาก companion object รักษาความเชื่อมโยงเชิงตรรกะกับคลาสเจ้าของ

ใช้ companion object สำหรับการจัดกลุ่มบริบทสแตติก — ค่าคงที่ โรงงาน เมธอดช่วยเหลือที่เกี่ยวข้องเชิงตรรกะกับคลาสแต่ไม่ต้องการอินสแตนซ์

ไวยากรณ์ของ companion object: การประกาศและการเข้าถึง

ไวยากรณ์พื้นฐานของ companion object นั้นง่าย: คีย์เวิร์ด companion ถูกวางไว้ก่อนการประกาศ object ภายในคลาส หากไม่ได้ระบุชื่อ ออบเจกต์จะได้รับชื่อ Companion ซึ่งสามารถเข้าถึงได้อย่างชัดแจ้งหรือโดยนัย

kotlin
class User {
    companion object {
        const val TABLE_NAME = "users"

        fun create(name: String): User {
            return User(name)
        }
    }

    val name: String
    constructor(name: String) { this.name = name }
}

// Access via class name
val table = User.TABLE_NAME
val user = User.create("Alice")

ในตัวอย่างข้างต้น TABLE_NAME และ create() สามารถเข้าถึงได้ผ่าน User.TABLE_NAME และ User.create() สังเกตตัวปรับแต่ง const สำหรับค่าคงที่ชนิดดั้งเดิม — มันรับประกันการแทรกค่าในเวลาคอมไพล์ คล้ายกับ Java static final สำหรับชนิดดั้งเดิมและ String

การเข้าถึงโดยนัยและชัดแจ้งผ่าน Companion

หาก companion object ไม่มีชื่อ สามารถเข้าถึงได้ผ่านชื่ออัตโนมัติ Companion: User.Companion.TABLE_NAME ในทางปฏิบัติแทบไม่จำเป็น แต่มีประโยชน์เมื่อเรียกจากโค้ด Java หรือเมื่อใช้รีเฟลกชัน

kotlin
// Both calls are equivalent
User.create("Bob")
User.Companion.create("Bob")

// Getting reference to companion object
val companion: User.Companion = User

Companion object ที่มีชื่อและคุณลักษณะ

companion object สามารถมีชื่อได้ ซึ่งช่วยเพิ่มความสามารถในการอ่านโค้ดและอนุญาตให้เข้าถึงด้วยชื่อที่มีความหมาย Factory เป็นชื่อที่พบบ่อยที่สุดสำหรับ companion object ที่ใช้เป็นโรงงาน

kotlin
class HttpResponse {
    companion object Factory {
        fun ok(body: String): HttpResponse = HttpResponse(200, body)
        fun notFound(): HttpResponse = HttpResponse(404, "Not Found")
        fun serverError(): HttpResponse = HttpResponse(500, "Internal Error")
    }
}

// Access via Factory name
val response = HttpResponse.Factory.ok("data")

companion object ที่มีชื่อช่วยบันทึกวัตถุประสงค์ของสมาชิกแบบสแตติก ในการปรับโครงสร้างโค้ด ชื่อทำให้โค้ดอธิบายตนเองได้ — นักพัฒนาเห็นทันทีว่า Factory สร้างอินสแตนซ์ของ HttpResponse

ข้อจำกัด: หนึ่ง companion object ต่อคลาสเท่านั้น

คลาสสามารถมี companion object ได้เพียงหนึ่งตัว นี่คือความแตกต่างพื้นฐานจาก Java ซึ่งคุณสามารถประกาศฟิลด์และเมธอดสแตติกจำนวนเท่าใดก็ได้โดยไม่ต้องจัดกลุ่ม หากจำเป็นต้องมีกลุ่มตรรกะหลายกลุ่มของสมาชิกสแตติก ให้ใช้การประกาศ object ที่ซ้อนกันโดยไม่มี companion

ตามเอกสารประกอบ Kotlin อย่างเป็นทางการ (Kotlin Docs, 2025) ข้อจำกัดหนึ่ง companion object มีแรงจูงใจจากการออกแบบภาษา: สมาชิกสแตติกควรผูกพันกับคลาสอย่างแน่นหนา และออบเจกต์คู่หูหนึ่งตัวให้ขอบเขตที่ชัดเจนสำหรับการเชื่อมต่อนี้ หากมีสมาชิกสแตติกมากเกินไป นั่นเป็นสัญญาณให้ปรับโครงสร้างคลาส

Companion object ในอินเทอร์เฟซ

ใน Kotlin อินเทอร์เฟซก็สามารถมี companion object ได้เช่นกัน ซึ่งอนุญาตให้กำหนดเมธอดสแตติกและค่าคงที่โดยตรงภายในอินเทอร์เฟซ ซึ่งเป็นสิ่งที่ไม่สามารถทำได้ใน Java แนวทางนี้มักใช้สำหรับการประกาศค่าคงที่ที่เกี่ยวข้องกับอินเทอร์เฟซหรือเมธอดโรงงาน

kotlin
interface ApiService {
    companion object {
        const val BASE_URL = "https://api.example.com"
        const val TIMEOUT_MS = 5000

        fun create(client: OkHttpClient): ApiService =
            Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .build()
                .create(ApiService::class.java)
    }
}

ในตัวอย่างนี้ BASE_URL, TIMEOUT_MS และ create() เป็นของอินเทอร์เฟซ ApiService ไม่ใช่การ implement ของมัน ซึ่งสะดวก: ค่าคงที่ทั้งหมดและตรรกะของโรงงานสำหรับสร้างอินสแตนซ์บริการ API อยู่ในที่เดียว — ภายในอินเทอร์เฟซเอง

การ implement อินเทอร์เฟซไม่สืบทอดสมาชิกของ companion object — พวกมันถูกเรียกผ่านชื่ออินเทอร์เฟซเท่านั้น: ApiService.BASE_URL

@JvmStatic และ @JvmField: ความเข้ากันได้กับ Java

เมื่อเรียกเมธอดของ companion object จากโค้ด Java โดยค่าเริ่มต้นพวกมันสามารถเข้าถึงได้เป็นเมธอดของคลาส Companion ที่ซ้อนกัน ไม่ใช่เป็นเมธอดสแตติกของคลาสภายนอก เพื่อส่งออกพวกมันเป็นเมธอดและฟิลด์สแตติกจริงสำหรับ Java ให้ใช้คำอธิบายประกอบ @JvmStatic สำหรับเมธอดและ @JvmField สำหรับฟิลด์

คำอธิบายประกอบการใช้งานผลลัพธ์ใน Java
@JvmStaticเมธอดของ companion objectเมธอดสแตติก: ClassName.method()
@JvmFieldฟิลด์ของ companion objectฟิลด์สแตติก: ClassName.field
constชนิดดั้งเดิมและ Stringค่าคงที่แบบอินไลน์: คอมไพเลอร์แทนที่ค่า
ไม่มีคำอธิบายประกอบเมธอด/ฟิลด์โดยค่าเริ่มต้นCompanion.method() / Companion.field
kotlin
class MathUtils {
    companion object {
        const val PI = 3.14159

        @JvmStatic
        fun square(x: Int): Int = x * x

        @JvmField
        val TAG: String = "MathUtils"
    }
}

// Called in Java as MathUtils.square(5), MathUtils.TAG

แนะนำให้ใช้ @JvmStatic สำหรับเมธอดสาธารณะทั้งหมดของ companion object ที่ควรเข้าถึงได้จากโค้ด Java const ใช้ได้กับชนิดดั้งเดิมและ String เท่านั้น — สำหรับชนิดอื่นให้ใช้ @JvmField

ตัวอย่างการใช้งาน companion object ในทางปฏิบัติ

ในโปรเจกต์จริง companion object ถูกใช้สำหรับหลายสถานการณ์มาตรฐาน เมธอดโรงงานเป็นรูปแบบที่พบบ่อยที่สุด: แทนที่จะใช้คอนสตรักเตอร์หลายตัวที่มีลายเซ็นต่างกัน จะใช้เมธอดโรงงานที่มีชื่อใน companion object

kotlin
sealed class NetworkResult<out T> {
    data class Success<out T>(val data: T) : NetworkResult<T>()
    data class Error(val message: String) : NetworkResult<Nothing>()

    companion object {
        fun loading<T>(): NetworkResult<T> =
            Loading()
    }
}

private class Loading<T> : NetworkResult<T>()

companion object ใน NetworkResult ให้เมธอดโรงงาน loading() ที่สร้างอินสแตนซ์ Loading ด้วยชนิดเจเนอริกที่ถูกต้อง หากไม่มีเมธอดนี้ คุณจะต้องสร้างอินสแตนซ์โดยตรงผ่าน Loading() ซึ่งเปิดเผยการ implement ภายใน

ค่าคงที่และแท็กการบันทึก

companion object มักใช้สำหรับเก็บค่าคงที่เฉพาะคลาส ค่าคงที่ที่ประกาศด้วย const val จะถูกแทรกในเวลาคอมไพล์ ทำให้ไม่มีค่าใช้จ่ายเพิ่มเติมในรันไทม์

kotlin
class UserRepository {
    companion object {
        private const val TAG = "UserRepository"
        private const val CACHE_SIZE = 100
        private const val DEFAULT_PAGE_SIZE = 20
    }
}

คำถามที่พบบ่อย

companion object แตกต่างจาก object ปกติใน Kotlin อย่างไร?

object ปกติเป็นซิงเกิลตันเต็มรูปแบบที่ดำรงอยู่อย่างอิสระจากคลาส companion object เป็น object ภายในคลาสซึ่งสมาชิกถูกเรียกผ่านชื่อคลาสภายนอก ในไบต์โค้ด companion object กลายเป็นคลาสซ้อนแบบสแตติก ในขณะที่ object ปกติกลายเป็นคลาสซิงเกิลตันอิสระ

สามารถสืบทอด companion object ได้หรือไม่?

ไม่ companion object ไม่ถูกสืบทอดโดยคลาสย่อย หากคลาส B สืบทอดคลาส A ดังนั้น B.method() จะไม่เรียกเมธอดจาก companion object ของคลาส A — คุณต้องเรียกผ่าน A.method() ซึ่งสอดคล้องกับพฤติกรรมของเมธอดสแตติกใน Java

จะเรียก companion object จากโค้ด Java ได้อย่างไร?

โดยค่าเริ่มต้น — ผ่าน ClassName.Companion.method() เพื่อเรียกเป็นเมธอดสแตติก ให้เพิ่ม @JvmStatic ให้กับเมธอดของ companion object สำหรับฟิลด์ ให้ใช้ @JvmField หรือประกาศด้วย const val สำหรับชนิดดั้งเดิม

ทำไม Kotlin ถึงไม่มีคีย์เวิร์ด static?

นักพัฒนา Kotlin ตัดสินใจละทิ้ง static เพื่อหันมาใช้ companion object สำหรับการทำงานที่เป็นหนึ่งเดียวกับออบเจกต์ static ใน Java ละเมิดหลักการ OOP เพราะเมธอดสแตติกไม่ผูกติดกับอินสแตนซ์ companion object เป็นออบเจกต์ระดับหนึ่งที่สามารถส่งต่อ ขยายด้วยฟังก์ชันส่วนขยาย และ implement อินเทอร์เฟซได้

companion object ส่งผลต่อประสิทธิภาพหรือไม่?

เมื่อใช้ const val สำหรับชนิดดั้งเดิมและ String — ไม่มีค่าใช้จ่ายเพิ่มเติม ค่าจะถูกแทรกในไบต์โค้ด สำหรับเมธอด ไม่มีค่าใช้จ่ายเพิ่มเติมเว้นแต่เมธอดจะเป็น inline ในกรณีส่วนใหญ่ companion object ไม่สร้างผลกระทบที่วัดได้ต่อประสิทธิภาพ ใช้ @JvmStatic เฉพาะสำหรับ API สาธารณะที่เรียกจาก Java

สรุป

  • companion object เป็นกลไกของ Kotlin สำหรับการประกาศสมาชิกสแตติกของคลาส แทนที่ Java static
  • หนึ่ง companion object ต่อคลาส — จัดกลุ่มองค์ประกอบสแตติกทั้งหมดในออบเจกต์คู่หูเดียว
  • การเข้าถึง ผ่านชื่อคลาส: ClassName.method() หรือ ClassName.Companion.method()
  • @JvmStatic และ @JvmField ส่งออกสมาชิกเป็นเมธอดและฟิลด์สแตติกจริงสำหรับ Java
  • const val สำหรับชนิดดั้งเดิมและ String — แทรกค่าในเวลาคอมไพล์โดยไม่มีค่าใช้จ่ายเพิ่มเติม
  • อินเทอร์เฟซ ก็รองรับ companion object สำหรับค่าคงที่และเมธอดโรงงาน
  • companion object ที่มีชื่อ ช่วยเพิ่มความสามารถในการอ่านโค้ดและอธิบายวัตถุประสงค์ของสมาชิกสแตติก

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม