Why do all secondary constructors have to call the primary constructor?

I am writing some classes that need to be instantiated from more than one object type on different occasions. These classes will all end up with the same fields no matter the object used to create it. For example I want to create a class instance from a Map that contains the values I need to set. I also want to create a class instance from another arbitrary object that also contains the same fields but naturally they must be acquired in a different way to the Map and the class is not the same type so cannot be passed into the same constructor.

I have a solution to this using a companion object and writing static methods to create instances of the class from each object. But I donā€™t like it very much.

What I would like to be able to do is have multiple constructors that take different objects as arguments and do not have to call the primary constructor, as these secondary constructors take totally different arguments and they cannot be shared with the primary constructor. I also do not want to have an empty primary constructor and set default values for all my class attributes.

If any given constructor can guarantee all the fields in the object are initialized, why must there be a primary constructor? In other words, why can we not have multiple primary constructors?

I donā€™t fully understand the problemā€¦
Can you show some of the static methods, so I can understand?

The fact that all other constructors have to call the primary constructor, makes the primary constructor the base case constructor.

The problem is, it is more natural to think of the primary constructor as the ā€˜main use caseā€™ constructor, which may not be the same as the base case.

I wonder if anyone has a solution ā€¦ such as allowing nominating a base case constructor.

If any constructor can guarantee that all the fields are initialized, then the constructors have these fields in common and you can create a (private?) primary constructor.

I do think of the primary-constructor as the most important constructor.
The other constructors are in most cases just handy for destructuring an object by yourself.
When the other constructors have bodies, I know itā€™s doing more then just destructuring the object.
I like this way of this implementation a lot.

You however donā€™t need a primary constructor:

class Test{
    val value: Any
    
   constructor(map: Map<String, Int>) {
       value = map
   }
   constructor(array: Array<String>) {
       value = array
    }
}

But I personally donā€™t like this option, as it makes you look at every body to figure out if it does something other than destructuring the param

The thing is, you donā€™t need to use a primary constructor. If you use ā€œsecondaryā€ constructors only you can have as many as you want (like Java). There is even easy refactoring in intellij Kotlin plugin for this (just stand on the constructor, alt-enter, and choose move to body). At that point there is no primary constructor and other constructors donā€™t need to call it (of course they still need to call a super constructor (maybe implicitly).

1 Like

My static methods are essentially doing the same thing as your two secondary constructors in your other comment. Thatā€™s what I am looking for. Although it may not be perfect, better than what I have. Didnā€™t know you could do that. Thanks!

youā€™re welcome.
Up to the next question :wink:

Hi to the topic of constructorsā€¦ I need help with this code:

class Foo(param: String, vararg others: Bar) : SomeInterface {
  private val objects = others.map { ... }.toList()
  private var fallback: SomeInterface? = null;

  constructor(fallback: SomeInterface, param: String, vararg others: Bar) : this(param, others) {
    this.fallback = fallback
  }
...

I need help to solve that. I declared the primary constructor where arguments are only used to initialze some properties. So I would like to provide a second constructorā€¦ but this how I coded does not work. How would you solve this problem?

Thanks!

If Iā€™m right, You arenā€™t struggling with the constructors but with varargs.
You need to add a spread operator before others:
change this(param, others) to this(param, *others).

I would change your code to:

class Foo(
    param: String,  
    vararg others: Bar,
    private var fallback: SomeInterface? = null
) : SomeInterface {
    private val objects = others.map { ... }.toList()
    ...
}

but thatā€™s just an uneducated advice, without having seen anything of your real codeā€¦

Hi,
Thank you very much! It is beautiful, but my compiler does not like it. Behind a vararg, you cannot put another parameter. When I want to instantiate an object, the compiler is expecting still a Bar object as argument. :expressionless:

yup, can add it as a named variableā€¦
This is indeed a bit less clean.
You can swap them, if you arenā€™t creating it in a lot of classes at the momentā€¦
(If the fallBack is a lambda, You can call the constructor of Foo in the following way: Foo("", Bar()){ ... }, which is why I put the fallback at the endā€¦