2015-12-30 2 views
2

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

abstract class Presenter<V> { 

    fun bind(view: V) { 
     ... 
    } 

    ... 
} 

И у меня есть реализации этих предъявителей:

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

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

class FolderChooserActivity : BaseView(), FolderChooserView { 

    @Inject lateinit var presenter: FolderChooserPresenter 

    // method of baseview 
    override fun onStart() { 
     super.onStart() 

     presenter.bind(this) 
    } 
} 

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

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

+0

'FolderChooserView '- это интерфейс, и все интерфейсы представления имеют общий интерфейс предков? –

+0

Добавление тега 'android' может помочь, если это обычная проблема в иерархиях классов Android, кто-то другой мог бы ее решить. Вероятно, если бы он был решен с помощью Java-дженериков, это было бы тем же решением для Kotlin. Возможно, вы захотите спросить об этом также с тегом 'java', а затем отредактируйте вопрос, чтобы сказать для Java или Kotlin, как бы вы решили эту проблему с generics, избегая при этом возможного неконтролируемого исполнения. Это не нужно ограничивать Котлин. –

+0

Да, это интерфейс. Нет, у них нет общего интерфейса предка. Но если это решит проблему и обойдется в кастинге, это вариант. –

ответ

0

Вы можете добавить две общие параметры вашего 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>() 
+0

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

+0

Я тоже занимался этим и имел правильное решение, которое оказалось сложным. См. [Presenter] (https://github.com/nhaarman/Triad/blob/44c20dbf44006bf4d0c0962d382f2daa4dfe75fd/triad/src/main/java/com/nhaarman/triad/Presenter.java), [Контейнер] (https: // github. com/nhaarman/Triad/blob/44c20dbf44006bf4d0c0962d382f2daa4dfe75fd/triad/src/main/java/com/nhaarman/triad/Container.java) и база [RelativeLayoutContainer] (https://github.com/nhaarman/Triad/blob/44c20dbf44006bf4d0c0962d382f2daa4dfe75fd /triad/src/main/java/com/nhaarman/triad/RelativeLayoutContainer.java) (в java). – nhaarman

+0

Это файлы старой версии моей библиотеки, где общие типы Presenter и View ссылаются друг на друга, чтобы обеспечить правильное поведение. К сожалению [это требует много своего рода сложного шаблона] (https://github.com/nhaarman/Triad/blob/44c20dbf44006bf4d0c0962d382f2daa4dfe75fd/triad-sample/app/src/main/java/com/nhaarman/triad/sample/notes /noteslist/NoteView.java). – nhaarman

0

Ответ от @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>() 
} 
Смежные вопросы