hyena
hyena

Reputation: 765

Kotlin Pass in a type that implements a parameterized interface

i have the following working Java code however when i convert it into Kotlin, the code does not compile

Java code

public interface Presenter<V> {
   void attachView(V view);
}

Abstract class implementing above interface

public abstract class BasePresenter<T> implements Presenter<T> {
    @Override
    public void attachView(T view) {
        this.view = view;
    }
}

Abstract class that takes a Type parameter which implements the interface above

public abstract class PresenterActivity<P extends Presenter> extends BaseActivity {

    protected P presenter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (presenter != null) {
        presenter.attachView(this);
        }
    }
}

A class extending BasePresenter

public class FooPresenter extends BasePresenter<BarView> {
    ...
}

A class extending PresenterActivity

public class SomeActivity extends PresenterActivity<FooPresenter> implements BarView {
   ....
}

Kotlin code converted from above Java Code

interface Presenter<in V> {
    fun attachView(view: V)
}

Abstract class implementing above interface

abstract class BasePresenter<V> : Presenter<V> {
    override fun attachView(view: V) {
        this.view = view
    }
}

Abstract class that takes a Type parameter which implements the interface above, am not sure if <P : Presenter<Any>> below, is the correct expression

abstract class PresenterActivity<P : Presenter<Any>> : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (presenter != null) {
            presenter!!.attachView(this)
        }
   }
}

A class extending BasePresenter

class FooPresenter : BasePresenter<BarView>() {
    ....
}

A class extending PresenterActivity

class SomeActivity : PresenterActivity<FooPresenter>(), BarView {
    ....
}

The Kotlin class PresenterActivity gives the following compilation error Error:(9, 44) Type argument is not within its bounds: should be sub-type of 'Presenter<Any>'

this means FooPresenter is not recorgnised as a sub-type of Presenter<Any>

am assuming Presenter<V> is a subclass of Presenter<Any>, i have a feeling am wrong

Upvotes: 2

Views: 1695

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170723

Your Java code actually has a type safety problem: PresenterActivity<P extends Presenter> uses a raw type and when you later write presenter.attachView(this) you don't know whether you can attach this, but raw type means the compiler doesn't warn you. Kotlin doesn't let you hide it like this.

am assuming Presenter<V> is a subclass of Presenter<Any>, i have a feeling am wrong

in actually means Presenter<V> is a super-type of Presenter<Any>, not a subtype. See https://kotlinlang.org/docs/reference/generics.html (or search for covariance and contravariance, there are a lot of explanations on Stack Overflow and otherwise, though more for Scala than Kotlin but the idea is the same).

I think the simplest solution would be something like

// concrete subclasses must extend V
abstract class PresenterActivity<V, P : Presenter<V>> : BaseActivity() {
   ... presenter!!.attachView(this as V)
}

and

class SomeActivity : PresenterActivity<BarView, FooPresenter>(), BarView {
    ....
}

Upvotes: 3

Related Questions