2

Why does this not compile?

class test
{
  constructor() {
      var a = Date().day
      this(a)
  }

  constructor(a:Int) {
  }
}

error is: Expression 'this' of type 'test' cannot be invoked as a function. The function 'invoke()' is not found.

The suggested fix is to add this:

private operator fun invoke(i: Int) {}

Why?

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
user3561494
  • 2,164
  • 1
  • 20
  • 33

3 Answers3

8

First, both of these constructors are secondary constructors. A primary constructor is one which is located outside of the body of the class.

Second, as described in the documentation, the correct syntax to call another constructor is as follows:

class Test {
    constructor() : this(1) { }

    constructor(a: Int) { }
}
yole
  • 92,896
  • 20
  • 260
  • 197
  • 1
    What if you need to do some calculations before calling the secondary constructor such as: class test { constructor() { var a = Date().day this(a) } constructor(a:Int) { } } – user3561494 Aug 02 '18 at 15:49
  • 3
    You can't do this. This is a JVM restriction. If this was possible, the object would exist in an uninitialized state during the execution of `var a = Date()`, and this is not allowed. – yole Aug 02 '18 at 15:54
  • wait... depending on what you calculate, you can, e.g. you may write: `constructor() : this(Date().day)` or maybe I just didn't understand your question correctly ;-) If it's a more complex calculation but you start from a new instance somewhere, you may use something like `constructor() : this(ComplexObject().let { /* complex function leading to Int */ })` – Roland Aug 02 '18 at 15:55
1
class test constructor(){ // primary constructor (The primary constructor is part of the class header: it goes after the class name (and optional type parameters))

    constructor(a: Int) : this() { // secondary constructor

    }
}

If you class have define primary constructor, secondary constructor needs to delegate to the primary constructor. See here.

I think primary constructor can not be called from secondary constructor.

You can think like this: secondary calls primary and primary calls secondary => endless loop => not possible

In your case, there are 2 secondary constructor, so you can do like

class test {

    constructor() : this(Date().day) // I see it quite like Java here https://stackoverflow.com/questions/1168345/why-do-this-and-super-have-to-be-the-first-statement-in-a-constructor

    constructor(a: Int) {
    }
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Linh
  • 57,942
  • 23
  • 262
  • 279
-2

Couple of things are wrong here:

  • Classes should always use camel-case for their names (test -> Test)
  • You cannot call another constructor as you tried to (calling this(1) inside of the other constructors body)

I think what you actually want is a being a property and alternatively initialize it with a default value. You could do it like this

class Test(val a: Int) {
    constructor() : this(1) // notice how you can omit an empty body
}

or even better, like this:

class Test(val a: Int = 1) // again an empty body can be omitted.

Edit:

If you need to do some calculations, as asked in the comment below Yole's answer:

class Test(val day: Int) {
    // you can use any expression for initialization
    constructor(millis: Long) : this(Date(millis).day) 
}

or if things get more complicated:

class Test(var day: Int) {
    // pass something (i.e. 1) to the primary constructor and set it properly in the body
    constructor(millis: Long) : this(1) { 
        // some code
        day = // initialize day
    }
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121