I want to initialize lateinit variable with 'by'.
How can I?
lateinit var test: int
fun main() {
test by something {} // error!
}
I tried using by in by lazy, and I tried using by in lateinit var, but it didn't work.
You don't need lateinit when using by lazy. Lazy means it'll be initialized the first time it's referenced. lateinit means you manually assign a value some time after construction.
So all you need is
val test by lazy { something() }
fun main() {
println(test) // runs the initializer and prints the value
}
Update:
Or, if you want to initialize an exising lateinit property:
lateinit var test: Type
fun main() {
val someting by other
test = something
}
Related
I have been trying to read the read the properties in the Kotlin code. The lateinit var gets the work done but since it is mutable, the value can be changed even after initialisation. I want to read a property from a file and to not worry about it being changed anywhere in the file. I want something like lateinit val which is not present in Kotlin; or you somehow able to add #Value inside by lazy block of code.
I am working with AWS Secret Manager so I am putting the same code here but my doubt is more generic and not specific to AWS.
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
I tried doing the similar thing with by lazy:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(awsRegion)
.build()
}
The above codes are working fine but it would be much cleaner if there's a way to merge these 2 lines:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}
In your specific case you can inject property directly into bean method as an argument (method arguments are immutable)
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(
#Value("\${aws.secretsManager.region}") region: String
): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
or if you need this property in multiple #Beans you can inject it into constructor of enclosing configuration class
#Confiuration
class SomeConfig(
#Value("\${aws.secretsManager.region}")
private val region: String
) {
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
}
I am trying to access to data class (Content)and I would like to use object(val isSelected: Boolean?)from PictureActivity. However, it causes UninitializedPropertyAccessException: lateinit property content has not been initialized. Do you know how to solve this situation? I used lateinit but I don't even know if using lateinit is the best way to access to data class(Content). If you know other way to access to it, please let me know.
The code is down below.
Content.kt
data class Content(
val id: Int,
val text: String,
val isSelected: Boolean?,
val url: String?
)
PictureActivity.kt
class PictureActivity : BaseActivity() {
private lateinit var binding: PictureActivityBinding
private lateinit var content: Content
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PictureActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
if(content.isSelected!!){
binding.button1.setOnClickListner{
startContentDownload(content.url!!)
return#setOnClickListener
}
}
private fun startContentDownload(url: String) {
//download image
}
}
}
lateinit keyword in Kotlin gives you an option to initialize it later but make sure you do it before you use.
To check if that variable is initialized or not, you can use below:
if(::content.isInitialized) {
// put your code here
}
In your case you have get data from somewhere(network call maybe) to fill in content data class, and then you will be able to use it.
You need to initialize the content variable first then only you can use it
content = Content(...)
please help me understand how to properly initialize "pets"
class Person:
class Person(
val height: Int,
val weight: Int,
val name: String,
) {
lateinit var pets: HashSet<Animal>
fun buyPet():Unit{
this.pets.add(Animal((0..100).random(), (0..100).random(), getRandomString((3..12).random())))
}
private fun getRandomString(length: Int) : String {
val allowedChars = ('A'..'Z') + ('a'..'z')
return (1..length)
.map { allowedChars.random() }
.joinToString("")
}
}
class Animal:
data class Animal(
val energy:Int,
val weight:Int,
val name:String) {
}
main:
fun main() {
val person1=Person(187, 85, "Denis")
person1.buyPet()
println(person1.pets)
}
I am getting this error
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property pets has not been initialized
at classes_06.Person.buyPet(Person.kt:35)
Replace
lateinit var pets: HashSet<Animal>
With
val pets = mutableSetOf<Animal>()
In the original code pets is declared, but never initialised, and lateinit is just telling the compiler "do not complain about this, I'll initialise it later". When doing Kotlin, try to avoid using lateinit as much as possible.
This other SO question might give you a bit more info in general terms about what is declaring and initialising a variable.
To complement Agusto’s answer:
Lateinit works in cases on which you can’t define the value of the variable immediately on the object construction, for any reason. Therefore, it expects you to define the value of the lateinit variable at some “late-r” point before you try to do anything else with it.
In your case it seems perfectly fine to define the variable “pets” in construction. So you can remove the lateinit modifier and define it right away as Agusto pointed out.
Or if need lateinit for any reason, you could check if it has been initialized before, and if not, define it in the buyPet() function before performing an “add” to it:
fun buyPet(): Unit{
if(!::pets.isInitialized) {
pets = mutableSetOf<Animal>()
}
this.pets.add(Animal((0..100).random(), (0..100).random(), getRandomString((3..12).random())))
}
See this question for further details about the isInitialized property
I am trying to use mockito in kotlin. I created the following test:
class HeroesDataSourceTest {
#Mock
lateinit var heroesRepository: HeroesRepository
#Mock
lateinit var params: PageKeyedDataSource.LoadInitialParams<Int>
#Mock
lateinit var callback: PageKeyedDataSource.LoadInitialCallback<Int, Heroes.Hero>
val hero = Heroes.Hero(1, "superman", "holasuperman", 1, null, null)
val results = Arrays.asList(hero)
val data = Heroes.Data(results)
val dataResult = Heroes.DataResult(data)
val compositeDisposable = CompositeDisposable()
lateinit var heroesDataSource: HeroesDataSource
private val heroesPublishSubject = PublishSubject.create<Heroes.DataResult>()
#Before
fun initTest(){
MockitoAnnotations.initMocks(this)
}
#Test
fun testLoadInitialSuccess(){
`when`(heroesRepository.getHeroes(ArgumentMatchers.anyInt())).thenReturn(heroesPublishSubject.singleOrError())
heroesDataSource = HeroesDataSource(heroesRepository, compositeDisposable)
val testObserver = TestObserver<Heroes.DataResult>()
heroesDataSource.loadInitial(params, callback)
heroesPublishSubject.onNext(dataResult)
testObserver.assertComplete()
}
}
But when I execute it in the line when(heroesRepository.getHeroes(ArgumentMatchers.anyInt())).thenReturn(heroesPublishSubject.singleOrError()) it just enter to getHeroes method instead of mocking it (and for sure since heroesRepository is not initialized because is mocket the method fails). I use this tons of times in java and it never gave me a single problem. What I have to do in kotlin to mock it properly?
EDIT
Here I put also HeroesRepository class
open class HeroesRepository {
val privateKey = "5009bb73066f50f127907511e70f691cd3f2bb2c"
val publicKey = "51ef4d355f513641b490a80d32503852"
val apiDataSource = DataModule.create()
val pageSize = 20
fun getHeroes(page: Int): Single<Heroes.DataResult> {
val now = Date().time.toString()
val hash = generateHash(now + privateKey + publicKey)
val offset: Int = page * pageSize
return apiDataSource.getHeroes(now, publicKey, hash, offset, pageSize)
}
fun generateHash(variable: String): String {
val md = MessageDigest.getInstance("MD5")
val digested = md.digest(variable.toByteArray())
return digested.joinToString("") {
String.format("%02x", it)
}
}
}
Without adding another dependency, you can replace using the #Mock annotation with a helper function somewhere:
inline fun <reified T> mock(): T =
Mockito.mock(T::class.java)
// To avoid having to use backticks for "when"
fun <T> whenever(methodCall: T): OngoingStubbing<T> =
Mockito.`when`(methodCall)
Then in your test:
val heroesRepository: HeroesRepository = mock()
#Test
fun yourTest() {
whenever(heroesRepository.getHeroes(ArgumentMatchers.anyInt()))
.thenReturn(heroesPublishSubject.singleOrError())
}
Like you had before. This should work, because you're not expecting Mockito to deal with #Mock lateinit var, which it seems to be struggling with, and instead instantiating the mock yourself.
FWIW, the reason that the when statement is just calling the actual function is because the function itself (getHeroes) is not marked as open.
So even though the class is non-final the method is final and not mocked.
Mockito is not quite compatible with kotlin, you can use mockito-kotlin resource instead.
Check out this reference:
https://github.com/nhaarman/mockito-kotlin
You can easily mock objects like so:
val heroesRepository = mock<HeroesRepository>()
Then you can use it as you use mockito
The below class has a very unique lifecycle, which requires me to temporarily null out lateinit properties
class SalesController : BaseController, SalesView {
#Inject lateinit var viewBinder: SalesController.ViewBinder
#Inject lateinit var renderer: SalesRenderer
#Inject lateinit var presenter: SalesPresenter
lateinit private var component: SalesScreenComponent
override var state = SalesScreen.State.INITIAL //only property that I want to survive config changes
fun onCreateView(): View { /** lateinit variables are set here */ }
fun onDestroyView() {
//lateinit variables need to be dereferences here, or we have a memory leak
renderer = null!! //here's the problem: throws exception bc it's a non-nullable property
}
}
Here's how it's used by the framework.
controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller
controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller
My lateinit properties are injected by dagger, and I need to set them to null in onDestroyView - or have a memory leak. This however is not possible in kotlin, as far as I am aware (without reflection). I could make these properties nullable, but that would defeat the purpose of Kotlin's null safety.
I'm not quite sure how to solve this. Ideally there could be some type of annotation processor that would generate java code to null out specific variables automatically in onDestroyView?
Kotlin lateinit properties use null as an uninitialized flag value, and there's no clean way to set null in the backing field of a lateinit property without reflection.
However, Kotlin allows you to override the properties behavior using delegated properties. Seems like there's no delegate that allows that in kotlin-stdlib, but if you need exactly this behavior, you can implement your own delegate to do that, adding some code to your utils:
class ResettableManager {
private val delegates = mutableListOf<ResettableNotNullDelegate<*, *>>()
fun register(delegate: ResettableNotNullDelegate<*, *>) { delegates.add(delegate) }
fun reset() { delegatesToReset.forEach { it.reset() } }
}
class Resettable<R, T : Any>(manager: ResettableManager) {
init { manager.register(this) }
private var value: T? = null
operator fun getValue(thisRef: R, property: KProperty<*>): T =
value ?: throw UninitializedPropertyAccessException()
operator fun setValue(thisRef: R, property: KProperty<*>, t: T) { value = t }
fun reset() { value = null }
}
And the usage:
class SalesController : BaseController, SalesView {
val resettableManager = ResettableManager()
#set:Inject var viewBinder: SalesController.ViewBinder by Resettable(resettableManager)
#set:Inject var renderer: SalesRenderer by Resettable(resettableManager)
#set:Inject var presenter: SalesPresenter by Resettable(resettableManager)
fun onDestroyView() {
resettableManager.reset()
}
}