Ответ от @nhaarman близок, но оставляет открытым отверстие, если связанный класс не является фактически видом представления.
Этот актер не такой уж плохой, на это не так много хороших ответов, если таковые имеются. Вы всегда можете добавить утверждение, которое дает действительно прямое сообщение об ошибке, и так как это происходит сразу же после создания активности, вы увидите ошибку очень рано в тестировании
Не думаю, что вы легко сможете добраться до лучший ответ, не создавая того, что управляет всеми частями отношений как единым. Я думаю, что его ответ имеет минимальный риск.
Compile время проверка, чтобы избежать ошибок во время выполнения
Вы могли бы написать функцию, которая может проверить недостающую часть во время компиляции, и люди могли бы использовать его как время компиляции утверждает.
// 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>()
}
'FolderChooserView '- это интерфейс, и все интерфейсы представления имеют общий интерфейс предков? –
Добавление тега 'android' может помочь, если это обычная проблема в иерархиях классов Android, кто-то другой мог бы ее решить. Вероятно, если бы он был решен с помощью Java-дженериков, это было бы тем же решением для Kotlin. Возможно, вы захотите спросить об этом также с тегом 'java', а затем отредактируйте вопрос, чтобы сказать для Java или Kotlin, как бы вы решили эту проблему с generics, избегая при этом возможного неконтролируемого исполнения. Это не нужно ограничивать Котлин. –
Да, это интерфейс. Нет, у них нет общего интерфейса предка. Но если это решит проблему и обойдется в кастинге, это вариант. –