Home > OS >  How to initialize lateinit property in kotlin. error "kotlin.UninitializedPropertyAccessExcepti
How to initialize lateinit property in kotlin. error "kotlin.UninitializedPropertyAccessExcepti

Time:12-25

I get the following error "kotlin.UninitializedPropertyAccessException: lateinit property student has not been initialized"

My code:

package com.example.a7_simpleroomapp


import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.navigation.findNavController
import com.example.a7_simpleroomapp.databinding.FragmentInputBinding
import kotlinx.coroutines.*

class InputFragment : Fragment() {
    private lateinit var appDb: AppDatabase


    @OptIn(DelicateCoroutinesApi::class)
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {

        val binding: FragmentInputBinding =
            DataBindingUtil.inflate(
                inflater,
                R.layout.fragment_input,
                container,
                false
            )
        appDb = AppDatabase.getDatabase(this.requireContext())

        binding.readButton.setOnClickListener {

            val zsid = binding.zsidText.text.toString()
            val name = binding.nameText.text.toString()
            val gender = binding.genText.text.toString()
            val school = binding.sclText.text.toString()

            if (zsid.isNotEmpty() && name.isNotEmpty() && gender.isNotEmpty() && school.isNotEmpty()) {

                val student = Student(
                    null, zsid, name, gender, school
                )
                GlobalScope.launch(Dispatchers.IO) {
                    appDb.StudentDao().insert(student)
                }


                binding.zsidText.text.clear()
                binding.nameText.text.clear()
                binding.genText.text.clear()
                binding.sclText.text.clear()
                Toast.makeText(context, "Successfully Added!", Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText(context, "Enter all the values to continue!", Toast.LENGTH_SHORT)
                    .show()
            }
        }

        binding.viewButton.setOnClickListener {
            val idzs = binding.zsidRead.text.toString()
            if (idzs.isNotEmpty()) {
                lateinit var student: Student
                GlobalScope.launch {
                    student = appDb.StudentDao().findByZsid(idzs)
                    Log.d("Data",student.name.toString())

                }
                    val nam = student.name.toString()
                    val gend = student.gender.toString()
                    val schl = student.school.toString()

                view?.findNavController()
                    ?.navigate(InputFragmentDirections.actionInputFragmentToDataFragment(zsid = idzs, name = nam, gender = gend, school = schl))
            } else {
                Toast.makeText(context, "Please enter any values to continue!", Toast.LENGTH_SHORT)
                    .show()
            }
        }
        return binding.root
    }
}

I don't know where and how to initialize the student lateinit variable... Please help me. I am just beginner,forgive my bad code

I tried to initialize it and it always thows error

CodePudding user response:

Its because, the coroutine will do it in asyn..! and you trying to access the value of Student before it gets initialized.

so change the viewButton as below code,

binding.viewButton.setOnClickListener {
        val idzs = binding.zsidRead.text.toString()
        if (idzs.isNotEmpty()) {
           //No need late init var. and try to use lifeCycle Aware CoroutineScope instead of GlobalScope(for good practice.) 
           //The Global Scope is not need in this Use Case. so if you want you can change "GlobalScope.launch" into "lifecycle.coroutineScope.launch"
            lifecycle.coroutineScope.launch(Dispatchers.IO) { //add lifecycle runtime dependency in gradle.
                
                var student: Student = appDb.StudentDao().findByZsid(idzs)
                Log.d("Data",student.name.toString())

                val nam = student.name.toString()
                val gend = student.gender.toString()
                val schl = student.school.toString()
             withContext(Dispatchers.Main){
            view?.findNavController()
                ?.navigate(InputFragmentDirections.actionInputFragmentToDataFragment(zsid = idzs, name = nam, gender = gend, school = schl))
            }
           }
        } else {
            Toast.makeText(context, "Please enter any values to continue!", Toast.LENGTH_SHORT)
                .show()
        }
    }

For more info about lifecycle aware coroutine scope , please check it - https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope

CodePudding user response:

You init it by setting it to a value- any value. But it needs to be done before you use it, or you get an error. Your problem is you're setting it in a coroutine. Coroutines are asynchronous. You can't do what you're trying to do with a lateinit var. Instead, everything that uses student should be in the coroutine as well.

Alternatively you can use runBlocking instead of launch to start the coroutine. That would make the rest of the code wait for that coroutine to finish to continue. However it would still happen asynchronously, so there would be a delay before the navigation.

  • Related