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.

1 Like

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)

2 Likes

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 (GitHub - ronmamo/reflections: Java runtime metadata analysis, 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

2 Likes

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.

2 Likes

Checked what @Wasabi375 said. It’s actually true.

package com.example.android.database

var qqq = 45.also { println("QQQ") }

fun zzz() {}

class Ttt {
    val rrrr = 4.also { println("ttt") }
}

with invocation in main activity:

        zzz()
        println("after zzz")
        qqq
        qqq
        Ttt()
        Ttt()

gives:

QQQ
after zzz
ttt
ttt

Notice only once printing QQQ.

code:

Ttt()

Prints

ttt

Invoking only zzz() also prints QQQ that means reading package properties.
Importing doesn’t invoke initialization (may be it gets stripped off on compile). Neither merely creating object Ttt() invoke initialization of package variables. (seems like class and vars are in different files)

IMHO: by policy any library shouldn’t execute any code by them own without asking it explicitly. For example, malicious library can leak your project data somewhere in that way. But, if you insist,
you should invoke that bar() function in all classes init blocks where you need it. That’s how it works if you don’t have a singe start point class.

1 Like

One thing to note is, that this is (as far as I can tell) not documented anywhere. This is an emergent behaviour based on the way the JVM works and how kotlin compiles top level properties. I don’t think this will ever change for kotlin JVM since this would most liekly require a breaking change to the JVM (which is super unlikely) but this might behave quite differently for Kotlin JS and Kotlin Native.

In any case, a library that needs some initialization code to be run shouldn’t rely on top level properties but instead provide an init function.


That sounds like I’m super untrustworthy :frowning:

Nah it’s great to have independent verification, especially for something so wiered :wink:

1 Like