Дженерики: абстрактный класс и тип ребенка

У меня есть абстрактный класс, называемый ведущим:

abstract class Presenter<V> { fun bind(view: V) { ... } ... } 

И у меня есть реализации этих докладчиков:

 class FolderChooserPresenter : Presenter<FolderChooserView>() { ... } 

И просмотрите классы, которые вызывают метод bind в указанной точке:

 class FolderChooserActivity : BaseView(), FolderChooserView { @Inject lateinit var presenter: FolderChooserPresenter // method of baseview override fun onStart() { super.onStart() presenter.bind(this) } } 

Я хотел бы архивировать, чтобы иметь базовый класс для классов, таких как FolderChooserActivity которые автоматически вызывают метод bind.
Глупо повторять эти призывы снова и снова во всех реализациях.

Мой подход состоит в том, чтобы иметь абстрактный класс, который расширяет BaseView который вызывает метод bind. Но это явно не работает, поскольку для класса привязки требуется реализация, а не абстрактный класс.

Вы можете добавить два BaseView параметра в свой класс BaseView и BaseView this V :

 open class Presenter<V> { fun bind(v: V) {} } open class BaseView<P, V> where P : Presenter<V> { lateinit var presenter: P fun onStart() { p.bind(this as V) } } 

Вы использовали бы это как

 class FolderPresenter : Presenter<FolderChooserView>() { } class FolderChooserView : BaseView<FolderPresenter, FolderChooserView>() 

К сожалению, вы не получите никакой помощи от своего компилятора, если вы смешаете параметры из-за неконтролируемого приведения:

 class SomeOtherView : BaseView<SomeOtherPresenter, FolderChooserView>() 

Ответ от @nhaarman близок, но оставляет открытым отверстие, если связанный класс не является фактически видом представления.

Этот актер не такой уж плохой, на это не так много хороших ответов, если таковые имеются. Вы всегда можете добавить утверждение, которое дает действительно прямое сообщение об ошибке, и поскольку это происходит сразу же после создания активности, вы увидите ошибку очень рано в тестировании

Я не думаю, что вы легко получите лучший ответ, не создавая того, что управляет всеми частями отношений как единым. Я думаю, что его ответ имеет минимальный риск.

Проверка времени компиляции во избежание ошибки времени выполнения

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

 // a function that is used when people want to check validity at compile time, // it does nothing but cause compiler error if wrong heirarchy fun <A: V, P: Presenter<V>, V : View> checkValid() { // empty on purpose, used for compile time check } // successful: checkValid<FolderChooserActivity, FolderPresenter, FolderChooserView>() // error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'" checkValid<TryingToFoolItActivity, FolderPresenter, FolderChooserView>() // this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known checkValid<SomeOtherActivity, FolderPresenter, FolderChooserView>() 

Использование функции для создания класса с проверкой времени компиляции, чтобы избежать ошибки времени выполнения

Вы также можете потребовать использования функции для создания класса *Activity . Но если вы не можете принуждать людей к тому, чтобы они имели нужный базовый класс, вы не можете принудительно использовать эту функцию. Здесь, во всяком случае, просто дать больше идей для этой проблемы.

 inline fun <reified A: V, P: Presenter<V>, V : View> makeActivity(): A { return A::class.java.newInstance() } // successful: val good1 = makeActivity<FolderChooserActivity, FolderPresenter, FolderChooserView>() // error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'" val bad1 = makeActivity<TryingToFoolItActivity, FolderPresenter, FolderChooserView>() 

Полный код:

Я размещаю полный код здесь, поэтому я могу изучить это больше и попытаться получить альтернативный полный ответ. Это действительно просто вариация ответа @ nhaarman.

 // sample classes class FolderPresenter : Presenter<FolderChooserView>() { } class BadPresenter : Presenter<RandomView>() { } // successful declaration class FolderChooserActivity : BaseActivity<FolderPresenter, FolderChooserView>(), FolderChooserView { } // Error: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.Presenter<test.renlearn.solrpref.FolderChooserView>'" class SomeOtherActivity : BaseActivity<BadPresenter, FolderChooserView>(), FolderChooserView {} // Runtime error, we are not a FolderChooserView class TryingToFoolItActivity : BaseActivity<FolderPresenter, FolderChooserView>() {} // now the version using a function to construct the activity, where // this function adds the missing step of compiler time validation. inline fun <reified A: V, P: Presenter<V>, V : View> makeActivity(): A { return A::class.java.newInstance() } // or a function that is used when people want to check validity at compile time, // it does nothing but cause compiler error if wrong heirarchy fun <A: V, P: Presenter<V>, V : View> checkValid() { // empty on purpose, used for compile time check } public fun foo() { // successful: val good1 = makeActivity<FolderChooserActivity, FolderPresenter, FolderChooserView>() // error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'" val bad1 = makeActivity<TryingToFoolItActivity, FolderPresenter, FolderChooserView>() // this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known val bad2 = makeActivity<SomeOtherActivity, FolderPresenter, FolderChooserView>() // successful: checkValid<FolderChooserActivity, FolderPresenter, FolderChooserView>() // error below: "Kotlin: Type argument is not within its bounds: should be subtype of 'test.so.FolderChooserView'" checkValid<TryingToFoolItActivity, FolderPresenter, FolderChooserView>() // this is blocked because SomeOtherActivity has its own compiler error so SomeOtherActivity type is not fully known checkValid<SomeOtherActivity, FolderPresenter, FolderChooserView>() } 
Intereting Posts
Можем ли мы использовать общие методы infix в Котлин? Передача массива ByteBuffer из Java / Kotlin в C ++ / V8 приводит к неправильным данным Ошибка: супертипы следующих классов не могут быть разрешены. Убедитесь, что у вас есть необходимые зависимости в пути к классам Как скомпилировать собственный код kotlin в iOS? Kotlin – Расходы на более высокие порядки? Создание регистратора на уровне пакета без класса Gradle не находит мои тесты с Kotlin и JUnit 5 Не удается создать проект с Android Studio 3.0 + DataBinding + Kotlin Android Kotlin ImageView binding Функции расширения для аннотированных типов Ошибка в android Studio 3.0 при синхронизации градиента. Ошибка. Причина. Не удалось найти допустимый путь сертификации для запрошенной цели. Сохранение объекта во внутренней памяти в android (kotlin) Как исправить инициализацию поля val с помощью прокси-сервера Spring Security? Как я могу запустить проверку кода AndroidStudio в терминале Проблемы с Kotlin с привязкой данных Android