Initialize global variable via function return value

Hi there,

in a Kotlin library project, I’m initializing a global / top-level variable like

val foo = bar()

If foo is not used anywhere else in the library, then bar() is not called. However, I need bar() to be called in any case. Is there a way (e.g. via some annotation) to force bar() to be called even if foo is unused? Or is there a better pattern to “automatically” run some code when a library is used?

1 Like

Maybe this isn’t a best practice, but you can write foo on a single line in your initialization code.

2 Likes

Not sure what you mean by “your initialization code”, but if I just white foo at the top-level on a single line I’m getting an error saying “Expecting a top level declaration”.

I think he means putting it at the start of your main function.

Well, there is no main in a / my “Kotlin library project” (see above). And if I had it, I could as well call bar() directly from there :wink:

Wooops, I missed the library part. I don’t think there is any way of enforcing initialization code to run with an annotation. I think the best you can do is provide an initMyLibrary function that every consumer has to call at least once.

In the JVM, static fields in a class are initialized when the class is first used. And Kotlin properties at the top level are stored in static fields.

So, once anything in that file is used (like a function, or something else), then the field will be initialized. If there’s a function that must be called in order to use your library, then putting it in the same file as that property will guarantee it’s initialized (and it’ll be initialized before the function’s called)

1 Like

No. This only works if top level functions or properties depend on it. If a class in the same file depends on it this approach doesn’t work because the class fill be compiled into a separate .class file from the top level properties.

I’ve run into this problem a few times myself, and I haven’t found any pretty or kotlinish solution, yet.
You can either enforce the call of the bar() in the contract of your library, but it is only a written contract, there is no compile time validation on using it.
Also you may check the variable in all your public functions, but that would pollute your code badly and it is error prone (if you left out your check in any of your functions).

So this is a feature one would really desire and it’s easy to show use cases for it. If a vote for library initializer would turn up, I vote for it. It would make life easier.

However, this mechanism is oppose how the java class loading works. The classloader would have to scan the whole classpath looking for init blocks and that’s resource consuming. That’s why it is not done and I think it won’t be done in the close future. That’s why all frameworks (like Spring) needs an entry point to be called implicitly.

When I have to do this kind of initialization, I use reflections library (https://github.com/ronmamo/reflections, at last it works on Java8+) and use annotations or collect all implementations of an interface in the classpath. But even then I need an entry point to start the scan. This is not perfect, one could miss the call of the entry point, but it works and is the only way at the moment.

1 Like

I mean if you have these in one file:

val foo = bar().also { println("foo initialized") }

fun bar(): Int = 6.also { println("bar() called") }

fun someOtherFun() {
    println("someOtherFun() called")
}

Then running this code from another file:

fun main() {
    println("main")
    someOtherFun()
}

Calling someOtherFun() will cause foo to be initialized, and bar() to be called. This will be printed:

main
bar() called
foo initialized
someOtherFun() called

Even though foo is never used, bar() is called. I’ve used this before when there’s some function in the library that the library consumer is guaranteed to call

1 Like

What I meant is that soemthing like this

class FooBar(){ ... }

val foo = bar().also { println("foo initialized") }

fun bar(): Int = 6.also { println("bar() called") }

// different file:
fun main() {
    FooBar()
}

will not initialize foo or call bar, because FooBar will be compiled into a separate class-file.