Общие типы и полиморфизм
У меня 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 также должно быть прекрасным, есть шанс, что он пройдет.