Poperton
Poperton

Reputation: 2127

Understanding Kotlin constructors and supertype initialization

I have this class:

class MyClass: Fragment(), AdapterView.OnItemSelectedListener {
    companion object {
        fun newInstance(parent: Activity) = MyClass(parent)
    }

    constructor(parent: Activity) {
        this.parent = parent;
    }

which was giving the error, on Fragment():

Supertype initialization is impossible without primary constructor

then I followed Supertype initialization is impossible without primary constructor and changed to

class MyClass constructor(): Fragment(), AdapterView.OnItemSelectedListener {
    companion object {
        fun newInstance(parent: Activity) = MyClass(parent)
    }

    constructor(parent: Activity) {
        this.parent = parent;
    }

but then I got

and the Idea made me change to

    constructor(parent: Activity) : this() {
        this.parent = parent;
    }

What does constructor means on class MyClass constructor(): Fragment()? Why did I have to put a this()?

Upvotes: 0

Views: 903

Answers (1)

user2340612
user2340612

Reputation: 10704

From the documentation:

A class in Kotlin can have a primary constructor and one or more secondary constructors. The primary constructor is part of the class header: it goes after the class name (and optional type parameters).

This is an example of primary constructor: class MyClass constructor()

The keyword constructor is mandatory only if you want to specify a visibility modifier (default is public) and/or an annotation, so all the following alternatives are equivalent:

class MyClass public constructor()
class MyClass constructor()
class MyClass()
class MyClass // parenthesis can be omitted if no argument is provided

Obviously the more concise version is preferred over the more verbose one, if that's the kind of constructor you want.

In addition to primary constructors you can also have zero, one or more secondary constructors. Secondary constructors must delegate to the primary constructor, either directly or indirectly - by calling another secondary constructor - Example: constructor(...) : this(...).

If your class doesn't specify any primary constructor, any secondary constructor must delegate to the super class. Example: e.g. constructor(...) : super(...).

If you don't specify any primary or secondary constructor, then your class will have an implicit default public constructor that takes no arguments (as in Java).

So, to answer your questions:

What does constructor means on class MyClass constructor(): Fragment()?

It declares the primary constructor, but given that you're not specifying any visibility modifiers, nor any annotations it can be omitted. The same applies to the parenthesis, hence it could be rewritten as class MyClass : Fragment() (note the : Fragment() bit declares your class extends Fragment and it specifies MyClass's default constructor should call its parent's empty constructor (more details here)

Why did I have to put a this()? That's mandatory for secondary constructors, as explained above.


 Edit

as explained in my comment below, in the first snippet of code you are:

  • defining a secondary constructor (the constructor(parent: Activity))
  • not defining a primary constructor. That's because you have a secondary constructor, but no parenthesis after your class definition (i.e. class MyClass() or class MyClass constructor() - they're equivalent, as described above), so Kotlin will not generate a default empty primary constructor for you
  • your class is extending Fragment and you're calling the parent's constructor as part of you class's signature. This requires a primary constructor for your class to be there, as highlighted by error you're facing: Supertype initialization is impossible without primary constructor.

 How to solve the problem?

One possible way is what you've done, i.e. defining both a primary and a secondary constructor. However, you'll be able to create an instance of MyClass in 2 ways: with or without passing a parent Activity. This is roughly equivalent to the following Java code:

class MyClass extends Fragment {
    private Activity parent;

    public MyClass() {
        super()
    }

    public MyClass(Activity parent) {
        super()
        this.parent = parent;
    }
}

Probably you don't want to construct that class without a parent Activity. If that's the case, just get rid of the secondary constructor – you don't need it – and create a primary constructor that accepts whatever argument you need. Example: class MyClass(val parent: Activity) : Fragment(). This is a very concise way to define a primary constructor that accepts an Activity in input and assigns that argument to an instance variable called parent; val means parent will be read-only (i.e. Kotlin will generate a getter but no setter), but if you need the instance variable to be mutable you can use var instead (i.e. Kotlin will generate both a getter and a setter).

This is roughly equivalent to the following Java code:

class MyClass extends Fragment {
    private Activity parent;

    public MyClass(Activity parent) {
        this.parent = parent;
    }
}

Upvotes: 3

Related Questions