Constructor Parameters

Given this example.

class MyClient(private val config: Config) {

  val configured = config.configureIt()
}

The constructor param becomes a class property (Automatically generated by Kotlin)

However in this case

class MyClient(config: Config) {

  val configured = config.configureIt()
}

The code still works, this time without a backing class property. That is, if I referenced the param from a function in the class, only the first example would compile.

E.g. this works in first example, but not second.

fun myFuction() {
  val configured = config.configureIt()
}

My question though, is “what” is the parameter in the first example? Its scope is more than just the constructor block or the init block. How should I think of a constructor parameter (not property) and its scope in my class?

I’m not sure what do you mean. You said yourself that in the first example “The constructor param becomes a class property”. Then you ask how a constructor parameter could be available in the function. It is not just a constructor param, it is both a param and a property.

Sorry, I wasn’t very clear.

I’m not concerned about the example where the parameter “becomes” a class property. It’s the example where it doesn’t that I’m not clear on.

For a constructor parameter, not declared with var or val, why/how is it addressable outside the scope of the constructor block or init block?

It is available in the constructor only. Initialization of properties: val configured = config.configureIt() also happens inside the constructor.

Primary constructor parameters can be used in the initializer blocks. They can also be used in property initializers declared in the class body:

It does… ? So the code, explicitly written outside of an init block, actually runs inside of an invisible constructor block?

Well, strictly speaking, from the Kotlin perspective there are only primary constructors, secondary constructors, initialization blocks and property initializers. Documentation doesn’t say any of these become a part of another one. But it says property initializers have access to primary constructor params. Internally, all these 4 types of items are joined together into a JVM constructor.

1 Like

Yes, initialisers and init blocks are run as part of the constructor. (Java works the same.)

Technically, all initialisers and init blocks are compiled into the code at the start of every constructor (in textual order, after the call to the superclass constructor but before the rest of the constructor code). (This means that if you have many constructors, any big initialisers will get duplicated in the bytecode.)

1 Like

Thank you both. That makes it a lot clearer.

On how java works, I guess the difference is you wouldn’t be able to reference any of the constructor params in the initialiser block. Kotlin is taking this a step further by actually wrapping initialisers and init blocks into the constructor scope.

1 Like

Please note Java doesn’t have the concept of the primary constructor, so a constructor which is always executed. Parameters may differ depending on which constructor was called, so it would be tricky to access these params outside of the constructor body.

1 Like

The other big difference (if you’re not aware of it) is that Kotlin executes all initialisation stuff from top to bottom. So primary constructor, then it literally goes in order. So if you have a property assignment, then an init block, then another property assignment, they are executed in that order.

In Java, as far as I understand it, all the properties are assigned first, then the constructor and init blocks run.

class Foo(val bar: String, baz: Int) {
    val bazDoubled = baz * 2

    init {
        println("bar: $bar")
        println("bazDoubled: $bazDoubled")
        println("capitalBar: $capitalBar") // <---- compile (or runtime) error, because this init block runs _before_ capitalBar gets assigned
        println(hello) // <---- compile (or runtime) error, because this init block runs _before_ hello gets assigned
    }

    val capitalBar = bar.upper()
    val hello = "Hello"
}
public class Foo {
    private final String bar;
    private final int bazDoubled;
    private final String capitalBar;

    public Foo(String bar, int baz) {
        this.bar = bar;
        this.bazDoubled = baz * 2;
        this.capitalBar = bar.toUpperCase(); // Pretend this is a real function, idk if it is
    }

    {
        System.out.println(hello); // <--- Works, because this runs after all field variables are assigned
    }

    private final String hello = "hello";
}
1 Like