1

#37 Kotlin Tutorial | Companion Object | Factory Pattern says you can use a Kotlin companion object when creating a class when it is complicated.

Companion Object | Factory Pattern

Kotlin Primary Constructor and Initializer Blocks shows how to use a Kotlin initializer block in a class.

fun main(args: Array<String>) {
    val person1 = Person("joe", 25)
}
class Person(fName: String, personAge: Int) {
    val firstName: String
    var age: Int
    // initializer block
    init {
        firstName = fName.capitalize()
        age = personAge
        println("First Name = $firstName")
        println("Age = $age")
    }
}

Why would you use the companion object to do this rather than the initializer block?

Regarding this question, I have no interest in Java.

CW Holeman II
  • 4,661
  • 7
  • 41
  • 72
  • 1
    Possible duplicate of [Constructors vs Factory Methods](https://stackoverflow.com/questions/628950/constructors-vs-factory-methods) – JB Nizet Aug 12 '19 at 17:14
  • You say you have no interest in Java, but the arguments are near-identical, and already well-discussed for the Java case. – charles-allen Aug 13 '19 at 07:24

2 Answers2

6

This practice of considering static factory methods (aka companion objects in Kotlin) over constructors (or init blocks in Kotlin) has been borrowed from the Java world.

The differences between static factory methods and constructors in Java are pretty much the same as the differences between companion objects and init blocks in Kotlin.

Here's a Medium article which describes the advantages of companion objects neatly. To summarize:

  1. Unlike constructors, they have names
  2. Unlike constructors, they do not require to create a new object each time they’re invoked
  3. Unlike constructors, they can return an object of any subtype of their return type
  4. They reduce verbosity of creating parameterized type instances

EDIT: Added AjahnCharles' comment as it's a valid use-case.

  1. They can return a null object, thereby indicating invalid params being passed for creating the object. ()

As with all design patterns, there are also disadvantages with companion objects:

  1. They cannot be used in subclasses construction
  2. They are not readily distinguishable from other static methods
dev
  • 11,071
  • 22
  • 74
  • 122
  • I have clarified the question as I have no interest in Java. (The article: Effective Java in Kotlin...). – CW Holeman II Aug 12 '19 at 20:48
  • 1
    The same points are valid, mostly (except for the names, I suppose). It's the usual static vs. instance considerations. There is only one (static) companion object; the code is run once. The code in an init block is run for each instance created. – Paul Hicks Aug 12 '19 at 21:24
  • I would add, a factory method could return a nullable, giving you the opportunity to cleanly reject invalid inputs. – charles-allen Aug 13 '19 at 07:23
2

The general idea behind factory methods is that you have some input to the factory method, execute some logic, and then have "output" that is the necessary parameters for the object constructor.

When you have only a single factory method, the main advantage of using it is that it lets your class expose only a single primary constructor that shows everything your class needs and everything your class retains. The logic to derive all of these properties is elsewhere. I personally think that this is nice, but it is admittedly not an extremely strong reason to prefer a factory method.

However, as soon as you have two different factory methods, the plan of using an initializer block becomes impossible. Even if you create a secondary constructor, any constructor of your class will execute all init blocks... so there's no way to have two "separate" factories using initializers.

The following is a very contrived example, but it does demonstrate behavior that would be impossible with only constructors and init blocks:

class Example(val text: String) {

    companion object {

        fun firstFactory(): Example {
            println("you called the first factory method")
            return Example("first")
        }

        fun secondFactory(): Example {
            println("you called the second factory method")
            return Example("second")
        }
    }
}
Ben P.
  • 52,661
  • 6
  • 95
  • 123