Reputation: 435
I know there are tons of similar questions in SO, but I'm afraid I can't find an analog case to mine. Please forgive me if there is one (and I would love a link :).
I have these two base classes, with self-bounded generics:
public interface View<V extends View<V, P>, P extends Presenter<V, P>> {
P getPresenter();
}
public abstract class Presenter<V extends View<V, P>, P extends Presenter<V, P>> {
protected V view;
public void takeView(V view) { this.view = view; }
}
The reason behind this is that every presenter know the exact class of its view and viceversa, so they can communicate invoking methods in each other without any fuss. (Each presenter will define its own interface for their views to implement, so the architecture is cleaner, but you know what I mean...)
If I implement them, there is no problem:
public class FooView implements View<FooView, FooPresenter> {
@Override
public FooPresenter getPresenter() {
FooPresenter p = new FooPresenter();
p.takeView(this); // Nice and clean!
return p;
}
}
public class FooPresenter extends Presenter<FooView, FooPresenter> {}
Then, I want to create an abstract base class for some kind of views:
public abstract class BaseView<V extends BaseView<V,P>, P extends Presenter<V,P>> extends SomeOtherBaseClass implements View<V, P>
But I need to do an unchecked cast when linking presenter and view!!!
@Override
public P getPresenter() {
P p = createPresenter(); // Another abstract, so every view can have its own presenter
p.takeView((V) this); // Doesn't compile without casting, the cast is marked as unchecked
return p;
}
As a funny aside, if I don't call takeView in BaseView, and do it in every concrete implementation, it works again...
public class BarView implements BaseView<BarView, FooPresenter> {
@Override
public FooPresenter getPresenter() {
FooPresenter p = createPresenter();
p.takeView(this); // javac likes this :/
return p;
}
}
Is there any way of avoiding the unchecked cast? I reckon is a limit in Java generics, but I may be the limited one. :D
Thanks a lot.
Upvotes: 1
Views: 515
Reputation: 1743
This will tie your classes strongly together, from what it appears only to be able to do double dispatch. I think you are hitting the boundary of what generics can do with this, but you can achieve the same differently.
this is therefor not a direct answer to your question but a possible alternative approach:
public static abstract class Presenter<V extends View> implements ViewTaker<V> {
protected V view;
public void takeView(V view) { this.view = view; }
}
public interface View { // can be a class as well
}
public interface ViewTaker <V extends View> {
public void takeView(V view);
}
On reading your code a second time, you have the view create it's presenter. This might be the limitation of pseudocode but this seems wrong to me, generally a presenter is constructed outside of the view, and the view is passed to it. (hence above code). Your specific concrete case might be different but then you probably need to post the actual code rather than the pseudocode.
I've tried your situation and then indeed I run into the same limit. it looks like
<P extends A<V>, V>
the 2 V's are seen as potentially different, eventhough they would be replaced by the same terms. I cannot come up with a situation (so far) where they could be different, so you would assume they'd refer to the same placeholder, but I guess there's no way around it.
I would recommend redesign and construct your Presenter outside of your View. Then your presenter can know of View without issue (see above). Alternatively construct your Presenter from view with a pojo version of the view. (View constructs object, feeds it to presenter). this can work if you only need to present a static version. Then presenter doesn't know View, but knows this representation.
Upvotes: 0
Reputation: 122489
Is there any way of avoiding the unchecked cast?
No, because the cast is unsafe.
Consider the following, which satisfies the bounds:
class FooView extends BaseView<FooView, FooPresenter>
class BarView extends BaseView<FooView, FooPresenter>
and then call getPresenter()
on a BarView
. The (V)this
cast would cast this
(a BarView
) to V
(FooView
), which is an invalid cast.
As a funny aside, if I don't call takeView in BaseView, and do it in every concrete implementation, it works again...
Not if you change the declaration to the following (which is valid):
class BarView implements BaseView<FooView, FooPresenter>
Then the compiler would complain about passing this
.
You can avoid the cast and make it safe, by adding an abstract method to BaseView
to get a value of type V
:
abstract public V getView();
Every implementation would have to implement this; for the classes where V
is themselves, e.g. FooView
, then can just return this;
for this function
class FooView extends BaseView<FooView, FooPresenter> {
public FooView getView() { return this; }
}
and then you can use this in BaseView.getPresenter()
, without needing to implement it in every subclass:
public P getPresenter() {
P p = createPresenter();
p.takeView(getView());
return p;
}
Upvotes: 2