Hampus
Hampus

Reputation: 2799

Using lateinit properties in constructor in kotlin

How can I use lateinit properties in my class constructor:

I have a spring component that I use to setup and access a third party library like so:

@Service
class LibProxy {

    @Value("\${lib.someProperty}")
    private lateinit var someProperty: String

    final var lib: Lib

    init {
        lib = Lib(someProperty)
    }
}

This gives a

kotlin.UninitializedPropertyAccessException: lateinit property someProperty has not been initialized

How is this supposed to be done?

I would like to avoid constructs like so:

@Service
class LibProxy {

    @Value("\${lib.someProperty}")
    private lateinit var someProperty: String

    private var lib: Lib? = null

    getLib(): Lib {
        if (lib == null) {
            lib = Lib(someProperty)
        }
        return lib ?: Lib(someProperty)
    }
}

Upvotes: 3

Views: 4310

Answers (2)

gidds
gidds

Reputation: 18577

Two possibilities here:

The easy one is to replace the init block with a @PostConstruct method.  Spring will call this once, after the object has been constructed (and hence autowired values are all set).  For example:

@PostConstruct
private fun initialise() {
    lib = Lib(someProperty)
}

The other is to arrange for the autowired property to be passed in a constructor, not set as a property (as per other answers).

One approach that works well, especially if you have several configuration properties, is to have a central class storing them:

@ConfigurationProperties("lib")
class ConfigProperties {
    var someProperty = "defaultValue"
    // …and other properties…
}

This will set someProperty from a "lib.someProperty" value in a config file (or left as "defaultValue" if not present).

You can then autowire its instance in the constructor, e.g.:

class SomeService @Autowired constructor(
    private val configProperties: ConfigProperties) {

    init {
        lib = Lib(configProperties.someProperty)
    }
}

(Centralising the properties can make it easier to find what properties are available, too.)

Upvotes: 3

Hampus
Hampus

Reputation: 2799

As per JEY's comment. This is how it is supposed to be done:

@Service
class LibProxy(@Value("\${lib.someProperty}") private val someProperty: String) {

    final var lib: Lib

    init {
        lib = Lib(someProperty)
    }
}

Upvotes: 4

Related Questions