Initializing object declarations with arguments?

I have a singleton that needs a parameter to initialize itself. I can provide that parameter during application startup, but I’m not sure how to code it. Is it possible?

object MySingleton {

    lateinit var arg: Thing

    private val foo = getFooDependsOnThing(arg)
    var bar: String by someDelegate(arg)
}

Or should I just do something like this?

class MySingleton(arg: Thing) {
    ...
    companion object {
        lateinit var shared: MySingleton
    }
}
// and on app startup...
MySingleton.shared = MySingleton(thing)

The second solution is better.

But remember: singletons are evil.

2 Likes

Why?

They are global variables, so come with all the disadvantages:

  • Your code makes assumptions about the environment it is running in.
  • It makes your code hard to test because you have to resort to tricks to stub/mock/… the singleton.

There are many cases where you only want 1 instance of an object. But in that case, simply create only one and pass it to the clients that need it.

Working with a shared object (whether there is only 1 instance or whether there are more) is cumbersome without dependency injection. Do not try to write a program without dependency injection, no matter how small it is.

Note that I think that the way dependency injection is done nowadays by most implementations (Spring, CDI, Dagger, etc.) does not bring the benefits touted by the dependency inversion principle. Because the specification of what to inject are a type plus optional qualifiers specified by the client code, they are basically still service lookups but the service lookup is performed by the framework instead of in the client code. And as service lookups are (if you cannot externally specify which service is looked up) basically global variables, you will not get the benefits (being able to modify the behavior of your program (almost) without touching existing classes) that come with “real” dependency injection.

1 Like

Theoritically you are absolutely right.
This is a concept most of the time one must follow. However, using dependency injection may also bring up its disadvantage.

There is a “magic” in the background. And one which is not always simple, or simple to follow. Most of the time it needs at least reflection, but sometimes even more deep magic such as using classpath scanners or even instrumentation creating proxy classes. It works like a charm as long as there is no problem. When it comes to debugging code, it quickly could become a nightmare. (Getting through proxy classes, for example. Or finding out why the injection failed or injected incorrect object.) Also, I’ve met (and sometimes has written myself :wink: ) usually smaller, but highly overcomplicated programs.

I don’t argue against DI as a whole, because it gives much ease for modularization and enforce light coupling. I use it often, but I learnt that any flexibility brings its cost and using a technique too eagerly could turn to be as disastrous as not using it at all.

When your system (being of any size) has globally accessible singletons, putting it in global variable is not as evil as it looks. (Especially, when you are not intend to change or replace implementation.) Kotlin learnt from Java and provided global variables and functions letting omit the meaningless, stateless, static utility classes.

One of the use case legitimating global singletons is avoid polluting your code by passing objects through several (maybe dozens) of functions. Usually this drove to the bad practice to store the reference into a private field to be accessible from any of the member functions. This is the case, when injection is a much better solution.

As a sidenote, having global variable even lets you change the implementation/object on the fly (although this may be a bad and dangerous practice). Factoring out DI is a pain, but factoring in later may be done in two steps: making the global variable a proxy/delegate to the injected value and later doing the DI.

As a conclusion, I am not arguing against dependency injection, but for the usefulness of global variables. Time to time, new techniques turn up and their prophets states loudly as the only right way to do things. My experience has taught me that most of the time there is not a single truth and the hardest thing is to find the correct tool for the actual problem.

I do not care about the amount of magic, but I care very much about how easy it is to understand a complex system (which I may or may not have written myself). I find that most current solutions make it cumbersome to figure out what gets injected (the combination of type-based injection, name-based injection, qualifiers, profiles, conditional beans, etc. makes it almost impossible to figure out what happens at run time). Luckily there is IntelliJ which helps a lot here.

My experience is different than yours. Most obstacles I encounter are there because things were not injected. This makes it impossible to solve using plain old object-oriented programming, and I have to resort to tricks which make the code a lot harder to understand.

1 Like

You said “most”. Is there a DI implementation that you like? I haven’t really used a DI framework. I looked at it years ago and the examples I saw didn’t seem much better (if any) than creating and connecting the objects in straight Kotlin code. I might convert if I saw an example where it simplified/clarified code.

The old Spring used to do it right: explicit wiring. But the syntax was very horrible (XML), and you were very limited in how objects could be created.

Because Kotlin is awesome, I was able to write a (simple) one myself: OOverkommelig. Its advantages are:

  • Terse syntax.
  • Everything is an object. No special syntax for strings, properties, environment variables, servlets, services, etc., so you have the full power of Kotlin to decide if and how an object must be created.
  • Reference-based: the dependency you need must be declared as a property or function, otherwise you will get a compiler error. Because of this find usages and go to definition in IntelliJ work fine, making it easy to figure out who uses an object and how an object is defined respectively. But the biggest advantage is that you can create multiple instances of the same type without having to resort to annotations, global names, etc. Just refer to the instance you want to use for a specific dependency.
  • Documentation in code (= an interface) of the external dependencies needed by a (sub) graph of objects. This makes it easy to read for developers, and makes it possible for the compiler to check that you satisfied all dependencies.
  • Cycles can easily be wired.
  • Simple life cycle per object: initialize and dispose.
  • No scopes. Scopes are easily represented by another object graph (which may get objects from an existing object graph injected).
  • No APT, reflection or annotations used.
  • Sub graphs are fully independent and can be reused (in another context, tests, etc.) without having to touch the sub graph, or the code of the objects created by the sub graph.

There are some examples so you can compare the code against the same code for Dagger and Guice, and some examples to show capabilities of OOverkommelig: https://github.com/squins/ooverkommelig/tree/master/examples/src/main/kotlin/org/ooverkommelig/examples

I have used it in a number of (relatively small: hundreds of objects) projects, and have not encountered problems with it. That said, there are a few things to keep in mind:

  • I really should put up more documentation, and create a release.
  • IntelliJ gets unbearably slow when handling sub graphs with more than a few dozen object definitions.
  • Because of the intensive use of lambdas, there will be lots of classes on the classpath.