Bird
Raised Fist0
Kotlinprogramming~15 mins

Companion objects as static alternatives in Kotlin - Deep Dive

Choose your learning style10 modes available
Overview - Companion objects as static alternatives
What is it?
In Kotlin, companion objects are special objects inside a class that let you create members similar to static ones in other languages. They allow you to call functions and access properties without creating an instance of the class. This helps organize code that belongs to the class itself, not to any specific object. Companion objects provide a clean and safe way to hold static-like members in Kotlin.
Why it matters
Without companion objects, Kotlin would lack a clear way to define static members like in Java or C#. This would make it harder to group utility functions or constants related to a class without creating unnecessary instances. Companion objects solve this by providing a dedicated place for such members, improving code clarity and performance. They help keep code organized and avoid mistakes from misusing instances.
Where it fits
Before learning companion objects, you should understand basic Kotlin classes and objects. After mastering companion objects, you can explore Kotlin's object declarations, singletons, and advanced features like extension functions and annotations. Companion objects are a bridge between instance members and static-like behavior in Kotlin.
Mental Model
Core Idea
A companion object is a special singleton inside a class that holds members accessible without creating class instances, acting like static members.
Think of it like...
Think of a companion object as a class's personal assistant who handles tasks that don't need the whole team (instances) to show up. The assistant works alone but always belongs to that class, ready to help anytime.
Class MyClass
├── Instance members (need object)
└── Companion object (singleton)
    ├── Static-like functions
    └── Static-like properties
Build-Up - 7 Steps
1
FoundationUnderstanding Kotlin classes and objects
🤔
Concept: Learn what classes and objects are in Kotlin and how instances work.
A class in Kotlin is like a blueprint for creating objects. Each object is an instance with its own data. For example: class Person(val name: String) { fun greet() = "Hello, $name!" } val p = Person("Anna") println(p.greet()) // Output: Hello, Anna! Here, greet() is an instance function, so you need an object to call it.
Result
You understand that instance members require creating objects to use them.
Knowing how instance members work helps you see why static-like members are useful for shared functionality.
2
FoundationStatic members in other languages
🤔
Concept: See how static members work in languages like Java to understand the problem Kotlin solves.
In Java, static members belong to the class itself, not instances: class Utils { static String greet() { return "Hello!"; } } You can call Utils.greet() without creating an object. Kotlin does not have static members directly, so it uses companion objects instead.
Result
You realize Kotlin needs a way to provide static-like members.
Understanding static members in other languages sets the stage for companion objects as Kotlin's solution.
3
IntermediateDeclaring companion objects in Kotlin
🤔
Concept: Learn how to declare a companion object inside a class and add members to it.
You declare a companion object inside a class using the keyword companion object: class MyClass { companion object { val info = "Static info" fun greet() = "Hello from companion" } } You can call MyClass.greet() and access MyClass.info without creating an instance.
Result
Companion object members are accessible like static members.
Knowing the syntax and usage of companion objects unlocks static-like behavior in Kotlin.
4
IntermediateAccessing companion object members
🤔
Concept: Understand how to call functions and properties inside companion objects from outside and inside the class.
From outside the class, you call companion members like this: println(MyClass.greet()) // Hello from companion println(MyClass.info) // Static info Inside the class, you can access them directly or via Companion: fun printInfo() { println(info) // Direct access println(Companion.info) // Using Companion } Companion is the default name but can be changed.
Result
You can use companion members easily without instances.
Understanding access rules helps avoid confusion and write cleaner code.
5
IntermediateCustom naming and interfaces for companion objects
🤔Before reading on: do you think companion objects can have custom names and implement interfaces? Commit to your answer.
Concept: Learn that companion objects can have custom names and implement interfaces to add flexibility.
You can rename the companion object: class MyClass { companion object Factory { fun create() = MyClass() } } Call with MyClass.Factory.create(). Companion objects can also implement interfaces: interface Creator { fun create(): MyClass } class MyClass { companion object : Creator { override fun create() = MyClass() } } This allows polymorphism and better design.
Result
Companion objects become more powerful and flexible.
Knowing companion objects can be customized and implement interfaces expands their use cases.
6
AdvancedCompanion objects and JVM static members
🤔Before reading on: do you think companion object members are compiled as static members on JVM? Commit to your answer.
Concept: Understand how companion objects are compiled to JVM bytecode and how to expose true static members.
On JVM, companion object members are compiled as instance members of a hidden singleton class named Companion. To expose true static members, use @JvmStatic annotation: class MyClass { companion object { @JvmStatic fun greet() = "Hello" } } This makes greet() a real static method in bytecode, improving Java interop and performance.
Result
You know how to optimize companion objects for JVM and Java callers.
Understanding compilation details helps write interoperable and efficient Kotlin code.
7
ExpertLimitations and pitfalls of companion objects
🤔Before reading on: do you think companion objects can fully replace static members in all scenarios? Commit to your answer.
Concept: Explore the limitations of companion objects and when they might not behave exactly like static members.
Companion objects are singletons but still objects, so: - They have a runtime instance, unlike pure static members. - Initialization order can cause surprises if companion accesses instance members. - Overusing companion objects can lead to cluttered classes. For pure constants, Kotlin recommends top-level declarations or const val. Understanding these limits helps avoid design mistakes.
Result
You recognize when companion objects are not the best choice.
Knowing companion objects' limits prevents misuse and encourages better architecture.
Under the Hood
Companion objects are compiled as static final singleton instances inside the class. The Kotlin compiler generates a hidden class named Companion with the companion's members as instance members. The outer class holds a static final field referencing this singleton. When you call a companion member, Kotlin accesses this singleton instance. The @JvmStatic annotation instructs the compiler to generate true static methods or fields in the outer class for better Java interop.
Why designed this way?
Kotlin avoids static members to keep the language consistent and object-oriented. Companion objects provide a controlled way to have static-like behavior while preserving Kotlin's design principles. This approach allows more flexibility, like implementing interfaces and inheritance, which static members cannot do. It also fits Kotlin's multiplatform goals, where static members may not exist.
Class MyClass
├─ static field: Companion INSTANCE
│
└─ Companion class (singleton)
   ├─ instance members (functions, properties)
   └─ @JvmStatic members (true static methods in MyClass)
Myth Busters - 4 Common Misconceptions
Quick: Do companion object members behave exactly like Java static members? Commit yes or no.
Common Belief:Companion object members are exactly the same as static members in Java.
Tap to reveal reality
Reality:Companion object members are instance members of a singleton object, not true static members, unless annotated with @JvmStatic.
Why it matters:Assuming they are identical can cause confusion about initialization, performance, and Java interoperability.
Quick: Can you create multiple companion objects in one Kotlin class? Commit yes or no.
Common Belief:You can have many companion objects in a single Kotlin class.
Tap to reveal reality
Reality:A Kotlin class can have only one companion object.
Why it matters:Trying to add multiple companion objects leads to compilation errors and design confusion.
Quick: Are companion objects initialized lazily or eagerly? Commit your guess.
Common Belief:Companion objects are initialized only when their members are accessed (lazy initialization).
Tap to reveal reality
Reality:Companion objects are initialized when the containing class is loaded, which may be eager depending on usage.
Why it matters:Misunderstanding initialization timing can cause unexpected side effects or performance issues.
Quick: Can companion objects hold state safely in multithreaded environments? Commit yes or no.
Common Belief:Companion objects are thread-safe by default for holding mutable state.
Tap to reveal reality
Reality:Companion objects are not inherently thread-safe; mutable state must be managed carefully.
Why it matters:Assuming thread safety can lead to subtle bugs in concurrent applications.
Expert Zone
1
Companion objects can implement interfaces, enabling polymorphism and design patterns like factories.
2
Using @JvmStatic selectively improves Java interoperability without losing Kotlin's object-oriented benefits.
3
Companion objects can be used as receivers for extension functions, allowing elegant DSLs and APIs.
When NOT to use
Avoid companion objects when you only need constants; prefer top-level const val declarations for better performance and clarity. For complex static utilities, consider object declarations or separate utility classes. Also, avoid companion objects for mutable shared state without proper synchronization.
Production Patterns
In production, companion objects often hold factory methods, constants, and static helpers. They are used to implement design patterns like Singleton, Factory, and Builder within a class. Teams use @JvmStatic to expose APIs cleanly to Java codebases. Companion objects also serve as holders for annotations and metadata related to the class.
Connections
Singleton pattern
Companion objects are a built-in Kotlin singleton tied to a class.
Understanding companion objects deepens knowledge of singletons and their controlled use within classes.
Static methods in Java
Companion objects provide Kotlin's alternative to Java static methods.
Knowing companion objects clarifies differences and similarities with Java static members, aiding cross-language understanding.
Module-level constants
Companion objects can hold constants, but module-level constants are often better.
Recognizing when to use companion objects versus top-level declarations improves code organization and performance.
Common Pitfalls
#1Trying to declare multiple companion objects in one class.
Wrong approach:class MyClass { companion object {} companion object {} }
Correct approach:class MyClass { companion object {} }
Root cause:Misunderstanding that Kotlin allows only one companion object per class.
#2Assuming companion object members are true static members on JVM without annotation.
Wrong approach:class MyClass { companion object { fun greet() = "Hi" } } // Called from Java as MyClass.greet(); // This fails
Correct approach:class MyClass { companion object { @JvmStatic fun greet() = "Hi" } } // Called from Java as MyClass.greet(); // Works
Root cause:Not using @JvmStatic to expose true static methods for Java interoperability.
#3Using companion objects to hold mutable shared state without synchronization.
Wrong approach:class MyClass { companion object { var counter = 0 } } // Multiple threads increment counter without locks
Correct approach:class MyClass { companion object { @Volatile var counter = 0 fun increment() { synchronized(this) { counter++ } } } }
Root cause:Ignoring thread safety when using shared mutable state in companion objects.
Key Takeaways
Companion objects in Kotlin provide a way to create static-like members tied to a class without needing instances.
They are singletons inside the class and can hold functions, properties, and implement interfaces for flexible design.
Companion objects differ from Java static members but can expose true static methods using @JvmStatic for better interoperability.
Only one companion object is allowed per class, and they are initialized when the class loads, not lazily.
Understanding companion objects helps write cleaner, more organized Kotlin code and avoid common pitfalls with static alternatives.

Practice

(1/5)
1. What is the main purpose of a companion object in a Kotlin class?
easy
A. To hold static-like members accessible without creating an instance
B. To create multiple instances of a class
C. To inherit properties from another class
D. To define instance variables for each object

Solution

  1. Step 1: Understand companion object role

    Companion objects allow defining members that belong to the class itself, not to instances.
  2. Step 2: Compare with instance members

    Instance variables belong to objects, but companion object members behave like static members accessible via the class name.
  3. Final Answer:

    To hold static-like members accessible without creating an instance -> Option A
  4. Quick Check:

    Companion object = static-like members [OK]
Hint: Companion objects act like static members in Kotlin classes [OK]
Common Mistakes:
  • Thinking companion objects create instances
  • Confusing companion objects with inheritance
  • Assuming companion objects hold instance variables
2. Which of the following is the correct syntax to declare a companion object inside a Kotlin class?
easy
A. object companion { val x = 10 }
B. companion object { val x = 10 }
C. static object { val x = 10 }
D. class companion { val x = 10 }

Solution

  1. Step 1: Recall Kotlin companion object syntax

    The correct keyword order is companion object inside the class.
  2. Step 2: Check each option

    companion object { val x = 10 } uses the correct syntax. Options A, B, and D use incorrect keywords or order.
  3. Final Answer:

    companion object { val x = 10 } -> Option B
  4. Quick Check:

    companion object syntax = companion object { val x = 10 } [OK]
Hint: Remember: 'companion object' keyword order is fixed [OK]
Common Mistakes:
  • Using 'static' keyword which Kotlin does not have
  • Swapping 'object' and 'companion' keywords
  • Declaring companion as a class
3. What will be the output of the following Kotlin code?
class MyClass {
    companion object {
        val number = 42
        fun greet() = "Hello"
    }
}

fun main() {
    println(MyClass.number)
    println(MyClass.greet())
}
medium
A. null\nnull
B. number\ngreet
C. Compilation error
D. 42\nHello

Solution

  1. Step 1: Understand companion object access

    Members inside companion object can be accessed using the class name without creating an instance.
  2. Step 2: Evaluate the print statements

    Printing MyClass.number outputs 42, and MyClass.greet() outputs "Hello".
  3. Final Answer:

    42\nHello -> Option D
  4. Quick Check:

    Companion members accessed via class name = 42 and Hello [OK]
Hint: Access companion members with ClassName.member [OK]
Common Mistakes:
  • Trying to create an instance to access companion members
  • Expecting compilation error due to missing instance
  • Confusing companion members with instance members
4. Identify the error in the following Kotlin code using companion objects:
class Example {
    companion object {
        fun show() = println("Show called")
    }
}

fun main() {
    val e = Example()
    e.show()
}
medium
A. Function show() must return a value
B. Missing companion object keyword
C. Cannot call companion object function on instance
D. Class Example cannot have companion objects

Solution

  1. Step 1: Analyze how companion functions are accessed

    Functions inside companion objects are accessed via the class name, not instances.
  2. Step 2: Check the call e.show()

    Calling show() on instance e causes an error because show() is not an instance method.
  3. Final Answer:

    Cannot call companion object function on instance -> Option C
  4. Quick Check:

    Companion functions accessed via class, not instance [OK]
Hint: Call companion functions with ClassName.function(), not instance [OK]
Common Mistakes:
  • Calling companion functions on instances
  • Forgetting companion object keyword
  • Expecting companion functions to be instance methods
5. You want to create a Kotlin class Calculator with a companion object that provides a static method add to add two integers. Which code snippet correctly implements this and allows calling Calculator.add(5, 3) without creating an instance?
hard
A. class Calculator { companion object { fun add(a: Int, b: Int) = a + b } }
B. class Calculator { fun add(a: Int, b: Int) = a + b }
C. object Calculator { fun add(a: Int, b: Int) = a + b }
D. class Calculator { static fun add(a: Int, b: Int) = a + b }

Solution

  1. Step 1: Identify correct static-like method declaration

    In Kotlin, static methods are created inside companion objects.
  2. Step 2: Evaluate each option

    class Calculator { companion object { fun add(a: Int, b: Int) = a + b } } correctly defines add inside a companion object. class Calculator { fun add(a: Int, b: Int) = a + b } defines an instance method, so requires an object. object Calculator { fun add(a: Int, b: Int) = a + b } uses an object declaration, which works but is not a companion object inside a class. class Calculator { static fun add(a: Int, b: Int) = a + b } uses 'static' keyword which Kotlin does not support.
  3. Final Answer:

    class Calculator { companion object { fun add(a: Int, b: Int) = a + b } } -> Option A
  4. Quick Check:

    Companion object method = class Calculator { companion object { fun add(a: Int, b: Int) = a + b } } [OK]
Hint: Use companion object for static methods inside classes [OK]
Common Mistakes:
  • Using 'static' keyword which Kotlin lacks
  • Defining method outside companion object
  • Using object declaration instead of companion object
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard