简体   繁体   中英

lateinit property gender has not been initialized

Using Databinding, ViewModel, LiveData ( MVVM ). In my Layout there's a form to add Employee Details.

layout:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
    <variable
        name="viewModelAddEmployee"
        type="com.app.roomemployeedemo.viewmodel.AddEmployeeViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/edt_lastname"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.AddEmployeeActivity">
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <EditText
                android:text="@{viewModelAddEmployee.firstname}"
                android:id="@+id/edt_firstname"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="40dp"
                android:ems="10"
                android:hint="Jaimin"
                android:inputType="textPersonName"
                android:maxLength="30"
                android:maxLines="1"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
            <EditText
                android:text="@{viewModelAddEmployee.lastname}"
                android:id="@+id/tv_lastname"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:ems="10"
                android:hint="Modi"
                android:inputType="textPersonName"
                android:maxLength="30"
                android:maxLines="1"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/edt_firstname" />
            <EditText
                android:text="@{viewModelAddEmployee.age}"
                android:id="@+id/edt_age"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:ems="10"
                android:hint="25"
                android:inputType="textPersonName"
                android:maxLength="3"
                android:maxLines="1"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_lastname" />
            <EditText
                android:text="@{viewModelAddEmployee.gender}"
                android:id="@+id/edt_gender"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:ems="10"
                android:hint="Gender (M/F, m/f)"
                android:inputType="textPersonName"
                android:maxLength="1"
                android:maxLines="1"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/edt_age" />
            <EditText
                android:text="@{viewModelAddEmployee.salary}"
                android:id="@+id/edt_salary"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:ems="10"
                android:hint="Salary (50000)"
                android:inputType="textPersonName"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/edt_gender" />
            <Button
                android:onClick="@{(v) -> viewModelAddEmployee.onAddEmployeeClick(v)}"
                android:id="@+id/button"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="40dp"
                android:layout_marginTop="40dp"
                android:layout_marginEnd="40dp"
                android:text="ADD NOW"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/edt_salary" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

Below is the ViewModel class for the same:

 class AddEmployeeViewModel(mContext: Context) : ViewModel(){
    var mContext=mContext
    lateinit var firstname:MutableLiveData<String>
    lateinit var lastname:MutableLiveData<String>
    lateinit var age:MutableLiveData<Int>
    lateinit var gender:MutableLiveData<Char>
    lateinit var salary:MutableLiveData<Int>
    lateinit var repositoryEmployee:EmployeeRepository
    init{
        repositoryEmployee= EmployeeRepository(mContext)
    }
    fun onAddEmployeeClick(view: View)
    {
        repositoryEmployee.onAddEmployeeClick(mContext,firstname,lastname,age,gender,salary)
    }
}

Initialized Viewmodel and binding utility in Activity class as below:

class AddEmployeeActivity : AppCompatActivity() {
lateinit var viewModelAddEmployee: AddEmployeeViewModel
lateinit var binding: ActivityAddEmployeeBinding
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewModelAddEmployee = ViewModelProvider(this,AddEmployeeFactory(this@AddEmployeeActivity)).get(AddEmployeeViewModel::class.java)
    binding = DataBindingUtil.setContentView(
        this@AddEmployeeActivity,
        R.layout.activity_add_employee
    )
    binding?.setLifecycleOwner(this)
    binding?.viewModelAddEmployee = viewModelAddEmployee
}

}

But, Getting below Error:

Caused by: kotlin.UninitializedPropertyAccessException: lateinit property gender has not been initialized
            at com.app.roomemployeedemo.viewmodel.AddEmployeeViewModel.getGender(AddEmployeeViewModel.kt:16)

What might be the issue? Especially with Char type gender!!

You are declaring your live data objects as lateinit var which means that you have to initialize them later before referring to them. You need to remove lateinit and initialize when creating the ViewModel or initialize each live data field in your init block like here:

firstname = MutableLiveData<>()

What you can do is this

fun returnName(): LiveData<String> {
        if (firstname == null) {
            firstname = MutableLiveData()
            //call some function to set value to the variable
        }
        return firstname
    }

This would ensure that you don't create multiple instances of that list and it would return the same instance eveytime. You can implement it on any variable you want

In kotlin you must initialize lateinit property. Add below line to oncreate -

 gender = MutableLiveData<Char>()

Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.

To handle this case, you can mark the property with the lateinit modifier:

Sample

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // dereference directly
    }
}

In your case, values must be loaded into variables in ViewModel.

In you case;

class AddEmployeeViewModel(mContext: Context) : ViewModel(){
    var mContext=mContext
    lateinit var firstname:MutableLiveData<String>
    lateinit var lastname:MutableLiveData<String>
    lateinit var age:MutableLiveData<Int>
    lateinit var gender:MutableLiveData<Char>
    lateinit var salary:MutableLiveData<Int>
    lateinit var repositoryEmployee:EmployeeRepository
        init{
              repositoryEmployee= EmployeeRepository(mContext)
              lastname = MutableLiveData<String>()
              firstname = MutableLiveData<String>()
              age = MutableLiveData<Int>()
              gender = MutableLiveData<Char>()
              salary = MutableLiveData<Int>()
        }
fun onAddEmployeeClick(view: View)
    {
        repositoryEmployee.onAddEmployeeClick(mContext,firstname,lastname,age,gender,salary)
    }
}

Best Practice

private val _sampleVar = MutableLiveData<String>()
val sampleVar: LiveData<String>
   get() = _sampleVar

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM