This tutorial explains what is Kotlin Companion Object, its syntax, and how to use it in your program with Java Code examples:
Kotlin provides a companion keyword that can be used to create static members for a class definition.
In other languages like Java and C#, using the static keyword to an individual function or parameter inside a class would make it a class-level variable and you would be able to access that function or parameter without an actual class instance.
In Kotlin, this behavior could be achieved using companion objects. We will see more details around how these can be declared, and when we should be using these kinds of objects.
=> Read ALL Our Kotlin Tutorials Here
Table of Contents:
Kotlin Companion Object
Syntax
The companion object can be declared within an existing class and the functions or parameters can be defined inside the companion object declaration.
class {className} (param1 , param2) { //member functions companion object { // params // functions } }
As you can see above, we can declare a companion object inside an existing class.
We can also have an explicit name for the companion object that’s declared for enhanced readability.
A few important points to remember while defining companion objects are:
- Companion objects cannot be declared outside a class.
- Companion objects can be used similar to static classes in other programming languages like Java and C#.
- These objects are common in all instances of the class.
- These objects are initialized when the class is instantiated.
Let’s understand this with the help of some examples
We will create an EmployeeData class – with params – empId and empName.
We will also create a companion object for this class – which would have a field – companyName initialized to “CompanyA” and company location initialized to “New York”.
Both these fields are common to all Employees, hence they are being declared as a part of the companion object.
class EmployeeData(var empId: Int, var empName: String) { // fields which are common across class objects companion object { val companyName = "CompanyA" val companyLocation = "New York" } } fun main() { // companion object fields accessed with {className}.{companion object parameter} println("Companion Object Data - ${EmployeeData.companyLocation} & ${EmployeeData.companyName}") }
//Output
Companion Object Data – New York & CompanyA
Pls, note that as above we can also access companion object fields using the Companion keyword or default object name.
So for example, to access companyName, we can also use
EmployeeData.Companion.companyName
However, the simplest way to use these parameters/functions is directly through the outer class name as we saw in the example above.
Let’s look at another way of declaring a companion object
We can declare a companion object with a name that can be accessed using
{className}.{companionObjectName}
class EmployeeData(var empId: Int, var empName: String) { // fields which are common across class objects companion object OrgData { val company Name = "CompanyA" val companyLocation = "New York" } } //Companion object fields can be accessed using EmployeeData.OrgData
Using Companion Objects with Java code
As discussed in the previous tutorials, we know that Java and Kotlin code are interoperable. i.e all Kotlin code is by default Java code.
In order to use the parameters/functions declared in companion object within Java code, you would need to annotate the methods/fields with annotation @JvmStatic
This can be thought of as an exact match for what would be a static field in the Java world.
Let’s try to understand this with the help of an example:
We will create the same companion object class with fields related to Employee Organization and annotate both the fields with @JvmStatic
class EmployeeData(var empId: Int, var empName: String) { // fields which are common across class objects companion object { @JvmStatic val companyName = "CompanyA" @JvmStatic val companyLocation = "New York" } }
Let’s declare a Java class with the name KotlinCompanionJvmStatic within the same project.
public class KotlinCompanionJvmStatic { public static void main(String[] args) { System.out.println("Company Location: " + EmployeeData.getCompanyLocation() + "Company Name:" + EmployeeData.getCompanyName()); } }
//Output
Company Location: New YorkCompany Name:CompanyA
As you can see above, we can access the EmployeeData static fields through the companion object with @JvmStatic annotation.
You can try removing this annotation, which will start showing an error in the Java code.
Let’s remove the annotation from companyName. You can navigate to the Java file and see the error related to this field.
Factory Pattern Using Companion Objects
Companion objects can be used to achieve the Factory Design Pattern in Kotlin.
In Object-Oriented Programming, a lot of times, creating objects of complex classes might be boilerplate and complex depending on the instantiation logic.
One such example – can be Database objects which might require a connection string in order to initialize them with the connection settings, URL, etc.
Let’s see how companion objects can be useful to implement Factory Pattern and make the instantiation of objects simpler.
We will have a database connection object, with a companion object declaration, which contains a method create() that would return a newly created object of the given class.
class DatabaseConnection(var connectionName: String) { var userName : String = "" fun getFullConnection() : String { return "$connectionName:$userName" } companion object { val HOST = "hostA" val PORT = "1234" // create method - used as a factory to create parent class obj fun createConnection() : DatabaseConnection = DatabaseConnection("$HOST:$PORT") } } // main function to call the factory method from companion object fun main() { val dbCon = DatabaseConnection.createConnection() dbCon.userName = "adam" println("Connection Name: ${dbCon.connectionName}") println("Connection Full Url: ${dbCon.getFullConnection()}") }
//Output
Connection Name: hostA:1234
Connection Full Url: hostA:1234:adam
Let’s try to understand the above example:
- We have a class DatabaseConnection which has connectionName and userName as its constructor and member parameters respectively and get a full connection method that returns the appended connection String.
- We have declared a companion object which contains some static variables – PORT and HOST and a function named createConnection which returns an object of type DatabaseConnection (i.e. parent class).
- In the main function, we have used the “createConnection” method to instantiate the object of type DatabaseConnection and separately defined initialized specific property like userName.
So, above, we are essentially creating an object of the type DatabaseConnection using the static method createConnection declared inside the companion object block.
In other words, the function createConnection is acting like a FACTORY for creating objects of the type DatabaseConneciton.
Frequently Asked Questions
Q #1) What is the difference between – object and companion object in Kotlin?
Answer: Companion objects are similar to objects, but they have a lot of differences.
Object | Companion object |
---|---|
Can be declared independently. | Companion objects need to be necessarily defined in another class. |
Objects are instantiated when they are called. | Companion objects are instantiated as soon as the containing class is initialized. |
Primarily used for providing Singleton behavior. | Companion objects are used as static equivalents in other languages. These are also referred to as class level variables. |
Q #2) Is Companion Object Singleton Kotlin?
Answer: Companion object is not a Singleton object or Pattern in Kotlin – It’s primarily used to define class level variables and methods called static variables. This is common across all instances of the class.
Singleton, on the other hand, is a pattern where you have just a single instance of class across its entire lifetime. In Kotlin, Singleton classes can be created using “object”.
Let’s see a simple example of creating Singleton using objects.
object Singleton { init { println("Singleton object instantiated!") } var name = "Name data" fun printDetails() { println(name) } } fun main() { Singleton.printDetails() }
//Output
Singleton object instantiated!
Name data
Q #3) Why does Kotlin need a Companion Object?
Answer: In Kotlin, there is no direct support of the “static” keywords similar to other programming languages like Java / C# – which is used to have class-level variables rather than instance-level variables.
In order to achieve the features of the static class, Kotlin provides “companion” objects which are nothing but 1 or more static fields or methods declared inside a class.
Q #4) How do you access variables in companion objects?
Answer: Since companion objects are equivalent to the Static class, any variables or methods can be directly accessed from the containing class.
Let’s understand this with the help of a simple code example.
class StudentData(var name: String, var age: Int) { init { noOfStudents += 1 } companion object StudentStats { private var noOfStudents : Int = 0 fun getStudentCount():Int = noOfStudents val SCHOOL_NAME = "St Stephens" } } fun main() { // accessing static methods (which uses private variable) var student1 = StudentData("Abhishek", 18) println(StudentData.getStudentCount()) var student2 = StudentData("Karthik", 18) println(StudentData.getStudentCount()) // accessing static parameter println(StudentData.SCHOOL_NAME) }
//Output
1
2
St Stephens
Q #5) What’s an INIT block in Kotlin?
Answer: In Kotlin, there’s a concept of primary and secondary constructors. Init block is used to extend Primary constructors where we can add any logic/checks/assignments which are required as a part of the object instantiation.
Some important points to note about the INIT blocks are:
- There can be one or many init blocks in the Kotlin class and these are executed in the sequence of declarations one after the other.
- INIT block is always called right after the Primary constructor.
Conclusion
In this tutorial, we learned about the concept of companion objects in Kotlin. These are used to provide static behavior to parameters or methods defined as part of the companion object.
Also, in order to have the Kotlin companion object used across the other Java code in the application, you can use @JvmStatic annotation to have interoperability with Java.
Static is a well-known concept across other widely used programming languages like Java and C# and is helpful to have methods or parameters that are common across all instances of the class.