companion object — ο μηχανισμός της γλώσσας Kotlin που αντικαθιστά τη λέξη-κλειδί static από τη Java για τη δήλωση στατικών μελών μιας κλάσης. Στην Kotlin δεν υπάρχει ενσωματωμένο static — αντίθετα χρησιμοποιείται δήλωση object με τον τροποποιητή companion μέσα στην κλάση. Σύμφωνα με το JetBrains, 2025, το companion object επιτρέπει την κλήση μεθόδων και ιδιοτήτων μέσω του ονόματος της κλάσης, καθιστώντας τα πλήρες αντίστοιχο των στατικών μελών στο οικοσύστημα Java.
Κύρια σημεία
companion object — είναι μια ειδική ποικιλία object declaration στη γλώσσα Kotlin, που σημειώνεται με τη λέξη-κλειδί companion. Επιτρέπει τη δήλωση μελών που ανήκουν στην κλάση, όχι στα στιγμιότυπά της — δηλαδή στατικά μέλη στην ορολογία Java. Σε αντίθεση με τη Java, όπου το static είναι τροποποιητής για μεμονωμένα πεδία και μεθόδους, στην Kotlin τα στατικά μέλη ομαδοποιούνται μέσα σε ένα αντικείμενο-σύντροφο.
Οποιαδήποτε κλάση στην Kotlin μπορεί να περιέχει ακριβώς ένα companion object. Τα μέλη αυτού του αντικειμένου είναι προσβάσιμα μέσω του ονόματος της κλάσης χωρίς δημιουργία στιγμιοτύπου: MyClass.method(). Ταυτόχρονα από άποψη bytecode, το companion object μεταγλωττίζεται σε ξεχωριστή εσωτερική κλάση και οι μέθοδοί του γίνονται στατικές μέθοδοι της εξωτερικής κλάσης όταν χρησιμοποιείται ο σχολιασμός @JvmStatic.
Σύμφωνα με το Google I/O 2024, το companion object έχει γίνει το κύριο πρότυπο για μεθόδους εργοστασίου, σταθερές και βοηθητικές συναρτήσεις σε σύγχρονες εφαρμογές Android σε Kotlin. Οι προγραμματιστές το επιλέγουν αντί για βοηθητικές κλάσεις με συναρτήσεις πακέτου, επειδή το companion object διατηρεί τη λογική σύνδεση με την κλάση-ιδιοκτήτη.
Χρησιμοποιήστε companion object για ομαδοποίηση στατικού περιβάλλοντος — σταθερές, εργοστάσια, βοηθητικές μεθόδους που λογικά ανήκουν στην κλάση αλλά δεν απαιτούν στιγμιότυπο.
Η βασική σύνταξη του companion object είναι απλή: η λέξη-κλειδί companion τοποθετείται μπροστά από τη δήλωση object μέσα στην κλάση. Αν δεν δοθεί όνομα, το αντικείμενο λαμβάνει το όνομα Companion, στο οποίο μπορεί κανείς να αναφερθεί ρητά ή σιωπηρά.
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 }
}
// Πρόσβαση μέσω ονόματος κλάσης
val table = User.TABLE_NAME
val user = User.create("Alice")
Στο παραπάνω παράδειγμα τα TABLE_NAME και create() είναι προσβάσιμα μέσω User.TABLE_NAME και User.create(). Παρατηρήστε τον τροποποιητή const για σταθερές πρωτόγονων τύπων — εγγυάται την ενσωμάτωση της τιμής κατά τη μεταγλώττιση, ανάλογα με το Java static final για primitive και String.
Αν το companion object δεν έχει όνομα, μπορεί κανείς να έχει πρόσβαση μέσω του αυτόματου ονόματος Companion: User.Companion.TABLE_NAME. Στην πράξη αυτό σπάνια χρειάζεται, αλλά είναι χρήσιμο κατά την κλήση από κώδικα Java ή κατά τη χρήση ανάκλασης.
// Και οι δύο κλήσεις είναι ισοδύναμες
User.create("Bob")
User.Companion.create("Bob")
// Λήψη αναφοράς σε companion object
val companion: User.Companion = User
Το companion object μπορεί να έχει όνομα, βελτιώνοντας την αναγνωσιμότητα του κώδικα και επιτρέποντας την πρόσβαση με ένα κατανοητό όνομα. Factory — το πιο κοινό όνομα για companion object που χρησιμοποιείται ως εργοστάσιο.
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")
}
}
// Πρόσβαση μέσω ονόματος Factory
val response = HttpResponse.Factory.ok("data")
Το ονομασμένο companion object βοηθά στην τεκμηρίωση του σκοπού των στατικών μελών. Στην αναδόμηση, το όνομα καθιστά τον κώδικα αυτο-τεκμηριωμένο — ο προγραμματιστής βλέπει αμέσως ότι το Factory δημιουργεί στιγμιότυπα HttpResponse.
Μια κλάση μπορεί να περιέχει μόνο ένα companion object. Αυτή είναι μια θεμελιώδης διαφορά από τη Java, όπου μπορούν να δηλωθούν οποιαδήποτε στατικά πεδία και μέθοδοι χωρίς ομαδοποίηση. Αν απαιτούνται πολλές λογικές ομάδες στατικών μελών, χρησιμοποιήστε ένθετες δηλώσεις object χωρίς companion.
Σύμφωνα με την επίσημη τεκμηρίωση της Kotlin (Kotlin Docs, 2025), ο περιορισμός σε ένα companion object δικαιολογείται από τον σχεδιασμό της γλώσσας: τα στατικά μέλη πρέπει να συνδέονται στενά με την κλάση και ένα αντικείμενο-σύντροφος παρέχει ένα σαφές όριο αυτής της σύνδεσης. Αν υπάρχουν πάρα πολλά στατικά μέλη — αυτό είναι σήμα για αναδόμηση της κλάσης.
Στην Kotlin, οι διεπαφές μπορούν επίσης να περιέχουν companion object. Αυτό επιτρέπει τον ορισμό στατικών μεθόδων και σταθερών απευθείας μέσα στη διεπαφή, κάτι που δεν είναι δυνατό στη Java. Αυτή η προσέγγιση χρησιμοποιείται συχνά για τη δήλωση σταθερών που σχετίζονται με τη διεπαφή ή μεθόδων εργοστασίου.
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, όχι στις υλοποιήσεις της. Αυτό είναι βολικό: όλες οι σταθερές και η λογική εργοστασίου για τη δημιουργία στιγμιοτύπου υπηρεσίας API βρίσκονται σε ένα μέρος — μέσα στην ίδια τη διεπαφή.
Οι υλοποιήσεις της διεπαφής δεν κληρονομούν τα μέλη του companion object — καλούνται μόνο μέσω του ονόματος της διεπαφής: ApiService.BASE_URL.
Κατά την κλήση μεθόδων companion object από κώδικα Java, είναι προεπιλεγμένα διαθέσιμες ως μέθοδοι της ένθετης κλάσης Companion, όχι ως στατικές μέθοδοι της εξωτερικής κλάσης. Για να τις εξαγάγετε ως πραγματικές static μεθόδους και πεδία για Java, χρησιμοποιήστε τον σχολιασμό @JvmStatic για μεθόδους και @JvmField για πεδία.
| Σχολιασμός | Εφαρμογή | Αποτέλεσμα σε Java |
|---|---|---|
| @JvmStatic | Μέθοδοι companion object | Στατική μέθοδος: ClassName.method() |
| @JvmField | Πεδία companion object | Στατικό πεδίο: ClassName.field |
| const | Primitive και String | Inline σταθερά: μεταγλωττιστής υποκαθιστά την τιμή |
| χωρίς σχολιασμό | Προεπιλεγμένες μέθοδοι/πεδία | Companion.method() / Companion.field |
class MathUtils {
companion object {
const val PI = 3.14159
@JvmStatic
fun square(x: Int): Int = x * x
@JvmField
val TAG: String = "MathUtils"
}
}
// Καλείται σε Java ως MathUtils.square(5), MathUtils.TAG
Η χρήση @JvmStatic συνιστάται για όλες τις δημόσιες μεθόδους companion object που πρέπει να είναι προσβάσιμες από κώδικα Java. Το const εφαρμόζεται μόνο σε primitive τύπους και String — για άλλους τύπους χρησιμοποιήστε @JvmField.
Σε πραγματικά έργα, το companion object χρησιμοποιείται για αρκετά τυπικά σενάρια. Μέθοδοι εργοστασίου — το πιο κοινό πρότυπο: αντί για πολλούς κατασκευαστές με διαφορετικές υπογραφές, χρησιμοποιούνται ονομασμένες μέθοδοι εργοστασίου στο companion object.
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 με τον σωστό generic τύπο. Χωρίς αυτή τη μέθοδο, θα έπρεπε να δημιουργήσετε στιγμιότυπο απευθείας μέσω Loading(), που αποκαλύπτει την εσωτερική υλοποίηση.
Συχνά το companion object χρησιμοποιείται για αποθήκευση σταθερών ειδικών για την κλάση. Οι σταθερές που δηλώνονται με const val ενσωματώνονται κατά τη μεταγλώττιση, παρέχοντας μηδενική επιβάρυνση κατά τον χρόνο εκτέλεσης.
class UserRepository {
companion object {
private const val TAG = "UserRepository"
private const val CACHE_SIZE = 100
private const val DEFAULT_PAGE_SIZE = 20
}
}
Συχνές Ερωτήσεις
Το ordinary object — είναι ένα πλήρες singleton που υπάρχει ανεξάρτητα από την κλάση. companion object — είναι ένα object μέσα στην κλάση του οποίου τα μέλη καλούνται μέσω του ονόματος της εξωτερικής κλάσης. Στο bytecode, το companion object γίνεται στατική ένθετη κλάση, ενώ το συνηθισμένο object γίνεται αυτόνομη singleton κλάση.
Όχι, το companion object δεν κληρονομείται από υποκλάσεις. Αν η κλάση B κληρονομεί την κλάση A, η B.method() δεν θα καλέσει μέθοδο από το companion object της κλάσης A — πρέπει να γίνει πρόσβαση μέσω A.method(). Αυτό αντιστοιχεί στη συμπεριφορά των στατικών μεθόδων στη Java.
Προεπιλεγμένα — μέσω ClassName.Companion.method(). Για να καλέσετε ως στατική μέθοδο, προσθέστε @JvmStatic στη μέθοδο companion object. Για πεδία χρησιμοποιήστε @JvmField ή δηλώστε τα με const val για primitive.
Οι προγραμματιστές της Kotlin αποφάσισαν να εγκαταλείψουν το static υπέρ του companion object για ομοιόμορφη εργασία με αντικείμενα. Το static στη Java παραβιάζει τις αρχές OOP, επειδή οι στατικές μέθοδοι δεν συνδέονται με ένα στιγμιότυπο. Το companion object — είναι αντικείμενο πρώτης τάξης που μπορεί να μεταβιβαστεί, να επεκταθεί με συναρτήσεις επέκτασης και να υλοποιήσει διεπαφές.
Κατά τη χρήση const val για primitive και String — μηδενική επιβάρυνση, η τιμή ενσωματώνεται στο bytecode. Για μεθόδους δεν υπάρχει επιβάρυνση αν η μέθοδος δεν είναι inline. Στις περισσότερες περιπτώσεις, το companion object δεν έχει μετρήσιμη επίδραση στην απόδοση. Χρησιμοποιήστε @JvmStatic μόνο για δημόσιο API που καλείται από Java.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης