9

A previous question shows how to put a static initializer inside a class using its companion object. I'm trying to find a way to add a static initializer at the package level, but it seems packages have no companion object.

// compiler error: Modifier 'companion' is not applicable inside 'file'
companion object { init { println("Loaded!") } }
fun main(args: Array<String>) { println("run!") }

I've tried other variations that might've made sense (init on its own, static), and I know as a workaround I can use a throwaway val as in

val static_init = {
    println("ugly workaround")
}()

but is there a clean, official way to achieve the same result?


Edit: As @mfulton26's answer mentions, there is no such thing as a package-level function really in the JVM. Behind the scenes, the kotlin compiler is wrapping any free functions, including main in a class. I'm trying to add a static initializer to that class -- the class being generated by kotlin for the free functions declared in the file.

Community
  • 1
  • 1
Dan
  • 4,312
  • 16
  • 28
  • Why not use `main` to do the initialization? – mfulton26 Aug 16 '16 at 18:26
  • Because I picked a bad toy example. Imagine a file full of related free functions that all depend on some shared initialization. Arguably I could just wrap those in a named `object`, but a lot of the beauty of kotlin is generally not being forced to do stuff like that. – Dan Aug 16 '16 at 18:30
  • Got it. Unfortunately I don't know of a way to do that other than your workaround or a variant. e.g. `val static_init = run { println("workaround") }` or `val static_init = object { init { println("workaround") } }`. – mfulton26 Aug 16 '16 at 19:35

3 Answers3

8

Currently there is no way to add code to the static constructor generated for Kotlin file classes, only top-level property initializers are getting there. This sounds like a feature request, so now there is an issue to track this: KT-13486 Package-level 'init' blocks

Another workaround is to place initialization in top-level private/internal object and reference that object in those functions that depend on the effect of that initialization. Objects are initialized lazily, when they are referenced first time.

fun dependsOnState(arg: Int) = State.run {
    arg + value
}

private object State {
    val value: Int
    init {
        value = 42
        println("State was initialized")
    }
}
Ilya
  • 21,871
  • 8
  • 73
  • 92
  • In Kotlin 1.1, you can use Kotlin Script (.kts) which will run code without needing a declaration. This is kind of similar to what the asker wants. – Jire Aug 18 '16 at 07:11
3

As you mentioned, you need a property with something that would run on initialisation:

val x = run {
    println("The package class has loaded")
}
voddan
  • 31,956
  • 8
  • 77
  • 87
0

I got around it by using a Backing Property on the top-level, under the Kotlin file. Kotlin Docs: Backing Properties

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // Type parameters are inferred
           // .... some other initialising code here
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }
Eric Liu
  • 1,516
  • 13
  • 19
  • If all you need is a single, lazily-initialized value like this, Kotlin has a builtin for you: `val table: MutableMap by lazy(::HashMap)` – Dan Jun 21 '17 at 15:46