Home > Enterprise >  Accidental override: The following declarations have the same JVM signature (getView()Landroid/view/
Accidental override: The following declarations have the same JVM signature (getView()Landroid/view/

Time:12-23

I am trying to convert a Java file to Kotlin just when I encounter this issue coming from the constructor parameter var view: View. I have seen similar issues but have not encountered one that uses a dialog as I have used here. It's important that I pass a view as a contractor parameter because I use that view for an important feature that depends on the dialog behavior.
Before converting to Kotlin

ackage com.indupendo.landing.ui.dialogs;

import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.indupendo.R;
import com.indupendo.databinding.DialogLoginBinding;
import com.indupendo.globals.utilities.Utils;

public class LoginDialog extends DialogFragment {

    DialogLoginBinding binding;
    View view;

    public LoginDialog(View view) {
        this.view = view;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        binding = DialogLoginBinding.inflate(getLayoutInflater());
        MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(requireActivity(),
                R.style.MaterialAlertDialog_rounded);
        dialogBuilder.setView(binding.getRoot());

        dialogBuilder.setNegativeButton("Cancel", (dialog, which) -> {
            Utils.INSTANCE.showLoginCancellationSnackBar(view, getLayoutInflater());
            dialog.cancel();
        });

        dialogBuilder.setPositiveButton("Login", (dialog, which) -> Toast.makeText(
                getActivity(),
                "Logged In",
                Toast.LENGTH_LONG).show());

        return dialogBuilder.create();
    }


    @Override
    public void onCancel(@NonNull DialogInterface dialog) {
        super.onCancel(dialog);
        Utils.INSTANCE.showLoginCancellationSnackBar(view, getLayoutInflater());
        dialog.cancel();
    }
}

After conversion

package com.indupendo.landing.ui.fragments

import android.app.Dialog
import com.indupendo.globals.utilities.Utils.showLoginCancellationSnackBar
import android.os.Bundle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.indupendo.R
import android.content.DialogInterface
import android.view.View
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import com.indupendo.databinding.DialogLoginBinding

class LoginDialog(var view: View) : DialogFragment() {
    var binding: DialogLoginBinding? = null
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        binding = DialogLoginBinding.inflate(layoutInflater)
        val dialogBuilder = MaterialAlertDialogBuilder(
            requireActivity(),
            R.style.MaterialAlertDialog_rounded
        )
        dialogBuilder.setView(binding!!.getRoot())
        dialogBuilder.setNegativeButton("Cancel") { dialog: DialogInterface, which: Int ->
            showLoginCancellationSnackBar(
                view, layoutInflater
            )
            dialog.cancel()
        }
        dialogBuilder.setPositiveButton("Login") { dialog: DialogInterface?, which: Int ->
            Toast.makeText(
                activity,
                "Logged In",
                Toast.LENGTH_LONG
            ).show()
        }
        return dialogBuilder.create()
    }

    override fun onCancel(dialog: DialogInterface) {
        super.onCancel(dialog)
        showLoginCancellationSnackBar(view, layoutInflater)
        dialog.cancel()
    }
}

How can I rewrite the Kotlin class and achieve the same result but without this bug?

CodePudding user response:

When you are using constructor with properties like this

class LoginDialog(var propertyName: View)

Kotlin is trying to create getters and setters for them. And their names will be getPropertyName() and setPropertyName().

In your example getter getView() is conflicting with the method from one of your base classes - Fragment.java:

/**
 * Get the root view for the fragment's layout (the one returned by {@link #onCreateView}),
 * if provided.
 *
 * @return The fragment's root view, or null if it has no layout.
 */
@Nullable
public View getView() {
    return mView;
}

You have several options how to fix this error:

  1. Just change the property name
class LoginDialog(var myView: View)
  1. Replace property initialization with simple parameter and init the property manually
class LoginDialog(view: View) : DialogFragment() {
var myView: View = view
    get() = field
    set(value) {
        field = value
    }
  1. Annotate your property with the @JvmField annotation. This will instructs the Kotlin compiler not to generate getters/setters for this property and expose it as a field
class LoginDialog(@JvmField internal val view: View) : DialogFragment() {

And a little off topic:

Though the options above could solve the error, there is more major issue with this class - sending a parameter to the Fragment constructor is a rather dangerous decision. From the Google spec:

All subclasses of Fragment must include a public no-argument constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the no-argument constructor is not available, a runtime exception will occur in some cases during state restore.

You can follow this thread for more details

CodePudding user response:

I took @DmitryArc's answer above into consideration and thought of a safer solution following their PS: "All subclasses of Fragment must include a public no-argument constructor." Instead of having to pass a constructor parameter, a way around this is having to access the view of the hosting activity, a button in my case, through requireActivity() function and findViewById(...).

package com.indupendo.landing.ui.dialogs

import android.app.Dialog
import com.indupendo.globals.utilities.Utils.showLoginCancellationSnackBar
import android.os.Bundle
import com.indupendo.R
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import android.content.DialogInterface
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import com.indupendo.databinding.DialogLoginBinding

class LoginDialog : DialogFragment() {
    var binding: DialogLoginBinding? = null
    private var snackBarView: Button? = null // This is required by snackBar as an argument

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        snackBarView = requireActivity().findViewById<View>(R.id.btn_login) as Button
        binding = DialogLoginBinding.inflate(layoutInflater)
        val dialogBuilder = MaterialAlertDialogBuilder(
            requireActivity(),
            R.style.MaterialAlertDialog_rounded
        )
        dialogBuilder.setView(binding!!.getRoot())
        dialogBuilder.setNegativeButton("Cancel") { dialog: DialogInterface, _: Int ->
            showLoginCancellationSnackBar(
                snackBarView!!, layoutInflater
            )
            dialog.cancel()
        }
        dialogBuilder.setPositiveButton("Login") { _: DialogInterface?, _: Int ->
            Toast.makeText(
                activity,
                "Logged In",
                Toast.LENGTH_LONG
            ).show()
        }
        return dialogBuilder.create()
    }

    override fun onResume() {
        super.onResume()
        snackBarView = requireActivity().findViewById<View>(R.id.btn_login) as Button
    }

    override fun onCancel(dialog: DialogInterface) {
        super.onCancel(dialog)
        showLoginCancellationSnackBar(snackBarView!!, layoutInflater)
        dialog.cancel()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
        snackBarView = null
    }
}

PS: Anyone is welcome to improve this answer!

  • Related