Общие типы и полиморфизм

У меня BaseFragment:

    public abstract class BaseFragment extends Fragment implements BaseMvpView {

        private BasePresenter presenter;

        protected void syncLifeCycle(BasePresenter presenter) {
            this.presenter = presenter;
            this.presenter.onCreate();
        }

        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);

            //noinspection unchecked
            presenter.onAttachView(this); //it works with a warning
        }

        @Override
        public void onResume() {
            super.onResume();
            presenter.onResume();
        }

        @Override
        public void onPause() {
            super.onPause();
            presenter.onPause();
        }

        @Override
        public void onDestroyView() {
            super.onDestroyView();
            presenter.onDetachView();
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            presenter.onDestroy();
        }

        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            presenter.onActivityResult(requestCode, resultCode, data);
    }
}  

и многие классы, которые его расширяют. Например MainFragment:

    public class MainFragment extends BaseFragment implements MainMvpView {

        MainPresenter<MainMvpView> presenter;

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            syncLifeCycle(presenter);
            //presenter.onCreate();
        }

        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            //presenter.onAttachView(this);
        }

        @Override
        public void onResume() {
            super.onResume();
            //presenter.onResume();
        }

        @Override
        public void onPause() {
            super.onPause();
            //presenter.onPause();
        }

        @Override
        public void onDestroyView() {
            super.onDestroyView();
            //presenter.onDetachView();
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            //presenter.onDestroy();
        }

        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            //presenter.onActivityResult(requestCode, resultCode, data);
        }
}   

Я хочу избежать повторения кода синхронизации жизненного цикла каждого фрагмента и презентатора. Поэтому я хочу реализовать этот процесс в BaseFragment. В Java эта строка presenter.onAttachView(this); работает, но с предупреждением "Непроверенный вызов onAttachView(V)" (я могу жить с этим). Но Kotlin не позволяет мне делать это вообще

abstract class BaseFragmentKotlin : Fragment(), BaseMvpView {

    private var presenter: BasePresenter<*>? = null

    //...

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        presenter?.onAttachView(this) //Does not work. "Out-projected type 'BasePresenter<*>?' prohibits the use of 'public abstract fun onAttachView(mvpView: V!): Unit defined in com.example.test.BasePresenter"
    }

//...
}

Мне действительно нужен совет, как это сделать правильно.

Отредактировано:

    public class BasePresenterImpl<V extends BaseMvpView> implements BasePresenter<V> {

        @Nullable
        public V mvpView;

        @Override
        public void onCreate() {

        }

        @Override
        public void onAttachView(V mvpView) {
            this.mvpView = mvpView;
        }

        @Override
        public void onResume() {

        }

        @Override
        public void onPause() {

        }

        @Override
        public void onDetachView() {
        mvpView = null;
        }

        @Override
        public void onDestroy() {

        }

        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {

        }
}

Вот весь тестовый код https://github.com/AlexNikolaTest/Test/tree/master/app/src/main/java/com/example/mytest

Ответ 1

Я думаю, что замена star-projection с помощью BaseMvpView помогла бы

abstract class BaseFragmentKotlin : Fragment(), BaseMvpView {

    private var presenter: BasePresenter<BaseMvpView>? = null

    //...

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        presenter?.onAttachView(this)
    }

//...
}

Причина в том, что Котлин различает параметры типа out и in (также называемые ковариантными и контравариантными) соответственно.)

Параметры типа

in указывают, что параметр типа должен потребляться родовым классом, т.е. использоваться как параметр функции, тогда как параметры типа out указывают, что общий класс будет выдавать значение пройденного тип параметр, то есть использоваться как возвращаемый тип для некоторой функции.

onAttachView(V mvpView) принимает параметр контравариантного типа, который подразумевает, что для V не может быть любого типа (он должен быть типа BaseMvpView или подкласса), так как вы потребляете это значение. То есть, если V было полностью неизвестно, мы не можем безопасно прочитать параметр, так как ожидается, что V будет экземпляром BaseMvpView. Однако, если бы это было так, что onAttachView производит, то есть. возвращаемый объект V, тогда будет работать проекция звезды.

Надеюсь, это поможет!

Ответ 2

Возможно, вы можете сделать это так:

interface IView

interface IPresenter {
    fun attachView(v: IView)
    fun detachView()
}

abstract class BasePresenter<V :IView> : IPresenter {
    protected var view: V? = null

    override fun attachView(v: IView) {
        this.view = v as V
    }

    override fun detachView() {
        view = null
    }
}

abstract class BaseFragment<P : IPresenter> : Fragment(), IView {
    protected lateinit var presenter: P

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        presenter.attachView(this)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        presenter.detachView()
    }
}

interface TestView : IView {
    fun doSomething()
}

interface TestPresenter : IPresenter {
    fun doSomething()
}

class TestPresenterImpl : BasePresenter<TestView>(), TestPresenter {
    override fun doSomething() {
    }
}

class TestFragment : BaseFragment<TestPresenter>(), TestView {

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        presenter = TestPresenterImpl()
        presenter.doSomething()
    }

    override fun doSomething() {
    }
}

Ответ 3

Вы могли бы попробовать это, тогда вы также можете иметь непроверенное предупреждение в Котлин, -)

    if (presenter != null) {
        val p = presenter as BasePresenter<BaseMvpView>
        p.onAttachView(this)
    }

В вашем MainFragment

    syncLifeCycle(presenter as BasePresenter<BaseMvpView>)

Не уверен, что он работает, просто немного поиграл в IntelliJ. Но так как дженерики стираются во время компиляции, и отлитие MainPresenter к BasePresenter также должно быть прекрасным, есть шанс, что он пройдет.