0
0
Kotlinprogramming~15 mins

Named companion objects in Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - Named companion objects
What is it?
In Kotlin, a companion object is a special object inside a class that allows you to call functions and access properties without creating an instance of the class. A named companion object is simply a companion object given a specific name instead of the default 'Companion'. This helps when you want to have multiple companion objects or want clearer code. It acts like a static member in other languages but with more flexibility.
Why it matters
Named companion objects let you organize code better and avoid confusion when a class needs more than one companion object or when you want to give a meaningful name to the companion. Without this, you would be limited to a single unnamed companion object, making code less clear and harder to maintain. This feature helps keep Kotlin code clean, readable, and scalable.
Where it fits
Before learning named companion objects, you should understand basic Kotlin classes and the concept of companion objects. After this, you can explore advanced Kotlin features like object declarations, static-like members, and factory patterns that use companion objects.
Mental Model
Core Idea
A named companion object is a special, named helper inside a class that holds functions and properties you can use without making an instance of that class.
Think of it like...
Imagine a company building where the main office is the class, and the companion object is like a special help desk inside the building. Naming the help desk lets visitors know exactly which help desk to go to when there are multiple desks.
Class MyClass
├── Named companion object MyHelper
│   ├── function helperFunction()
│   └── property helperValue
└── Instance members
    ├── function instanceFunction()
    └── property instanceValue
Build-Up - 6 Steps
1
FoundationUnderstanding basic companion objects
🤔
Concept: Introduce the idea of companion objects as a way to hold static-like members inside a class.
In Kotlin, companion objects let you define functions and properties that belong to the class itself, not to instances. For example: class MyClass { companion object { fun greet() = "Hello" } } You can call MyClass.greet() without creating MyClass.
Result
Calling MyClass.greet() returns "Hello".
Understanding companion objects is key because they let you group class-level functions and properties neatly inside the class.
2
FoundationDefault companion object naming
🤔
Concept: Explain that if you don't name a companion object, Kotlin names it 'Companion' by default.
When you write: class MyClass { companion object { fun greet() = "Hi" } } Kotlin automatically names the companion object 'Companion'. You can access it as MyClass.Companion.greet().
Result
MyClass.Companion.greet() returns "Hi".
Knowing the default name helps you understand how Kotlin references companion objects behind the scenes.
3
IntermediateIntroducing named companion objects
🤔Before reading on: do you think Kotlin allows more than one companion object per class? Commit to your answer.
Concept: Show how to give a companion object a custom name and clarify that only one companion object is allowed per class.
You can name a companion object like this: class MyClass { companion object Helper { fun greet() = "Hey" } } Now you call MyClass.Helper.greet() instead of MyClass.Companion.greet(). Note: Kotlin allows only one companion object per class, but naming it helps clarity.
Result
MyClass.Helper.greet() returns "Hey".
Naming the companion object improves code readability and helps when you want to avoid the generic 'Companion' name.
4
IntermediateAccessing named companion objects
🤔
Concept: Explain how to access members of a named companion object and how it differs from the default name.
With a named companion object, you use the name to access its members: class MyClass { companion object Factory { fun create() = MyClass() } } You call MyClass.Factory.create() to get a new instance. This is clearer than using MyClass.Companion.create().
Result
MyClass.Factory.create() returns a new MyClass instance.
Using a meaningful name for the companion object makes the code self-explanatory and easier to maintain.
5
AdvancedNamed companion objects and interfaces
🤔Before reading on: do you think a named companion object can implement interfaces? Commit to your answer.
Concept: Show that named companion objects can implement interfaces, enabling more flexible designs.
You can make a named companion object implement an interface: interface Factory { fun create(): T } class MyClass { companion object FactoryImpl : Factory { override fun create() = MyClass() } } Now MyClass.FactoryImpl.create() returns a new MyClass.
Result
MyClass.FactoryImpl.create() returns a new MyClass instance.
Knowing that companion objects can implement interfaces unlocks powerful design patterns like factories and singletons.
6
ExpertReflection and named companion objects
🤔Before reading on: do you think the name of a companion object affects how Kotlin reflection finds it? Commit to your answer.
Concept: Explore how named companion objects appear in Kotlin reflection and how their names matter for advanced use cases.
Using Kotlin reflection, you can find companion objects by their names: val companion = MyClass::class.companionObject val namedCompanion = MyClass::class.nestedClasses.find { it.simpleName == "FactoryImpl" } The name helps identify the companion object precisely, which is important in frameworks or libraries that use reflection.
Result
Reflection can find the named companion object by its name.
Understanding reflection behavior with named companions helps when building libraries or tools that inspect Kotlin classes.
Under the Hood
At runtime, Kotlin compiles companion objects into static fields inside the class. The named companion object becomes a static final field with the given name. This allows access without creating an instance. The JVM sees companion objects as singleton instances tied to the class, enabling static-like behavior with object-oriented flexibility.
Why designed this way?
Kotlin companion objects were designed to replace static members from Java with a more powerful and flexible concept. Naming companion objects allows clearer code and better interoperability with Java and reflection. The single companion object per class rule keeps the design simple and avoids ambiguity.
Class MyClass
├── static field: FactoryImpl (companion object instance)
│   ├── functions and properties
└── instance members

Access:
MyClass.FactoryImpl.function()  ← static access
MyClass().instanceFunction()   ← instance access
Myth Busters - 4 Common Misconceptions
Quick: Can a Kotlin class have multiple companion objects? Commit to yes or no.
Common Belief:A class can have many companion objects, each with different names.
Tap to reveal reality
Reality:Kotlin allows only one companion object per class, but you can name it anything you want.
Why it matters:Trying to add multiple companion objects causes compilation errors and confusion about where to put static-like members.
Quick: Does naming a companion object change how you call its members? Commit to yes or no.
Common Belief:Naming a companion object does not affect how you access its members; you always use 'Companion'.
Tap to reveal reality
Reality:When you name a companion object, you must use that name to access its members instead of 'Companion'.
Why it matters:Using the wrong name leads to compilation errors and confusion about where functions are defined.
Quick: Are companion objects the same as static members in Java? Commit to yes or no.
Common Belief:Companion objects are exactly the same as Java static members.
Tap to reveal reality
Reality:Companion objects are singleton objects with instance-like behavior, not just static members. They can implement interfaces and have state.
Why it matters:Misunderstanding this limits how you use companion objects and misses their full power in Kotlin.
Quick: Does the name of a companion object affect Kotlin reflection? Commit to yes or no.
Common Belief:The name of a companion object is irrelevant for reflection; it's always found the same way.
Tap to reveal reality
Reality:Reflection can find companion objects by their specific names, which is important for advanced uses.
Why it matters:Ignoring this can cause bugs in libraries or tools that rely on reflection to find companion objects.
Expert Zone
1
Named companion objects can implement multiple interfaces, enabling complex factory or provider patterns.
2
The companion object name becomes part of the JVM bytecode, affecting interoperability with Java and reflection-based frameworks.
3
Using named companion objects can improve code clarity in large projects where multiple static-like helpers exist.
When NOT to use
Avoid named companion objects when you only need simple static functions; a default companion or top-level functions may be clearer. Also, if you need multiple static-like groups, consider using separate objects or packages instead of multiple companion objects, since Kotlin allows only one companion object per class.
Production Patterns
In production, named companion objects are often used to implement factory patterns, singleton providers, or to group related static utilities with clear names. They also appear in libraries that require interface implementations on companion objects for plugin or serialization frameworks.
Connections
Static members in Java
Named companion objects provide a more flexible alternative to Java's static members.
Understanding named companion objects helps grasp how Kotlin improves on Java's static system by allowing objects with state and behavior.
Singleton pattern
Companion objects are a built-in way to create singletons tied to a class.
Knowing companion objects clarifies how Kotlin simplifies singleton usage without extra boilerplate.
Factory design pattern
Named companion objects often implement factory interfaces to create class instances.
Recognizing this connection shows how Kotlin companion objects support common design patterns naturally.
Common Pitfalls
#1Trying to declare multiple companion objects in one class.
Wrong approach:class MyClass { companion object One {} companion object Two {} }
Correct approach:class MyClass { companion object NamedCompanion {} }
Root cause:Misunderstanding that Kotlin allows only one companion object per class.
#2Accessing a named companion object using 'Companion' instead of its name.
Wrong approach:MyClass.Companion.someFunction() // when companion is named 'Helper'
Correct approach:MyClass.Helper.someFunction()
Root cause:Assuming the companion object name is always 'Companion' even when renamed.
#3Using companion object when a top-level function would be simpler.
Wrong approach:class Utils { companion object { fun add(a: Int, b: Int) = a + b } }
Correct approach:fun add(a: Int, b: Int) = a + b
Root cause:Overusing companion objects for simple utilities instead of Kotlin's top-level functions.
Key Takeaways
Named companion objects in Kotlin let you give a meaningful name to the single companion object inside a class.
They allow you to organize static-like functions and properties clearly and can implement interfaces for flexible designs.
Only one companion object is allowed per class, but naming it improves code readability and interoperability.
Understanding how to access named companion objects and their role in reflection is important for advanced Kotlin programming.
Using named companion objects wisely helps write clean, maintainable, and scalable Kotlin code.