how should i access the lateinit property - kotlin

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

Related

How to initialize lateinit var with 'by keyword' in kotlin

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
}

Kotlin Abstract Val Is Null When Accessed In Init Before Override

In Kotlin, accessing an abstract val in an init block causes a NullPointerException since the field is overridden by an extending class after the super class's init block executes.
The ideal solution would be a way to declare some code/function to execute after all stages of object instantiation are complete. I can only think of creating an initialize() function and manually calling it, which is bad because it's not automatic. Sticking it in init block doesn't work as shown in the below example.
As a comment pointed out below, instead of overriding fields, they can be passed in as parameters, but that doesn't work for my actual use-case. It adds a lot of clutter for object construction and is a nightmare when other classes try to extend it.
Below example shows a solution using coroutines. Waiting for a field to != null works in this case, but doesn't not when map is an open val with a default value that may or may not get overridden.
The problem is somewhat solved, but the solution is far from optimal. Any suggestions and alternative solutions would be greatly appreciated.
#Test #Suppress("ControlFlowWithEmptyBody", "SENSELESS_COMPARISON")
fun abstractValAccessInInitNPE() {
val key = "Key"
val value = "Value"
abstract class Mapper {
abstract val map: HashMap<String, String>
fun initialize() { map[key] = value }
}
// Test coroutine solution on abstract mapper
println("CoroutineMapper")
abstract class CoroutineMapper: Mapper() {
init {
GlobalScope.launch {
while (map == null) {}
initialize()
}
}
}
val coroutineMapper = object : CoroutineMapper() {
override val map = HashMap<String, String>()
}
val start = System.nanoTime()
while (coroutineMapper.map.isEmpty()) {} // For some reason map == null doesn't work
println("Overhead: ${(System.nanoTime() - start) / 1000000.0} MS")
println("Mapped: ${coroutineMapper.map[key].equals(value)}")
// Test coroutine solution on open mapper
println("\nDefaultMapper")
open class DefaultMapper: Mapper() {
override val map = HashMap<String, String>()
}
val newMap = HashMap<String, String>()
val proof = "Proof"
newMap[proof] = proof
val defaultMapper = object: DefaultMapper() {
override val map = newMap
}
Thread.sleep(1000) // Definitely finished by the end of this
println("Mapped: ${defaultMapper.map[proof].equals(proof) && defaultMapper.map[key].equals(value)}")
// Basic solution (doesn't work)
println("\nBrokenMapper")
abstract class BrokenMapper: Mapper() {
init { initialize() } // Throws NPE because map gets overridden after this
}
val brokenMapper = object: BrokenMapper() {
override val map = HashMap<String, String>()
}
println("Mapped: ${brokenMapper.map[key].equals(value)}")
}
An open (as all abstract functions are) function should never be called from a constructor because then the class's initial state cannot be guaranteed in the superclass. It can lead to all kinds of very tricky bugs.
Usually there's a good way to design around this problem if you take a step back. For instance, instead of making the map an abstract property, make it a constructor parameter in the superclass. Then you know it's already initialized before subclass constructors can try to use it.
abstract class Mapper(key: String, value: String, val map: HashMap<String, String>)
abstract class DecentMapper(key: String, value: String, map: HashMap<String, String>) : Mapper(key, value, map) {
init {
map[key] = value
}
}
val key = "Key"
val value = "Value"
val decentMapper = object : DecentMapper(key, value, HashMap()){
//...
}

How can I access to data class object from activity?

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(...)

Kotlin `object` initialization order leads to unexpected null instance

Consider the following code:
sealed class DataType<T : Any> {
abstract fun inputToType(input: String): T
abstract fun typeToSql(value: T): String
companion object {
val all = listOf(StringDt, LongDt)
}
}
object StringDt : DataType<String>() {
override fun inputToType(input: String) = input
override fun typeToSql(value: String) = "\"${value}\""
}
object LongDt : DataType<Long>() {
override fun inputToType(input: String) = input.toLong()
override fun typeToSql(value: Long) = value.toString()
}
val dataTypeList = listOfNotNull(StringDt, LongDt)
println(dataTypeList)
println(DataType.all)
Things to consider:
object as per documentation (and my understanding as well) is singleton and always instantiated
the two objects (StringDt and LongDt) are quite similar
The result of println(DataType.all) shows that one of the objects are not initialized. How is that possible? I would expect all the list elements to be initialized.
IntelliJ version: CE 2020.2
Kotlin plugin version: 1.4.0-release-IJ2020.2-1
Here's a running example which shows that the static list has a null element, while the non-static one contains both objects initialized.
It happens due to cyclical static initializations. It's pretty hard to explain this problem in two words but you can read about it here.
To fix this behavior you can change all initialization like this:
val all by lazy { listOf(StringDt, LongDt) }

Delegate optional argument resolution in Kotlin

In Kotlin, if I have a function that calls a constructor (or another function) is there a way to delegate the determination of default argument to the underlying constructor/function?
This syntax isn't valid, but hopefully this illustrates what I'm trying to do:
fun buildMyObject(stringParam: String?, intParam: Int?): MyClass {
return MyClass(stringParam, intParam)
}
class MyClass(val stringParam: String = "Hello world!", val intParam: Int = 42)
There's no support for exactly what you want. However, if you have a bit of flexibility about where buildMyObject lives, you can have something like:
class MyClass(val stringParam: String = defaultString, val intParam: Int = defaultInt) {
private constructor(stringParam: String?, intParam: Int?) : this(
stringParam ?: defaultString,
intParam ?: defaultInt
)
companion object {
private const val defaultString = "Hello world!"
private const val defaultInt = 42
fun buildMyObject(stringParam: String?, intParam: Int?): MyClass {
return MyClass(stringParam, intParam)
}
}
}
This has a couple of nice characteristics:
Lets users still call the null-safe MyClass constructor
Doesn't duplicate where the default values are defined
Doesn't grow exponentially in the number of constructors you'd need if you'd try to use a when and call with or without each param (imagine what would happen if you'd add a third parameter!)
Keeps the nullable constructor private, so callers can only use nullables if they go through the factory method