![](/img/trans.png)
[英]Incompatible Generic Types: Cannot specify sub-type when using .class with generic type
[英]Understanding generics: Incompatible types when class has generic type and implements one of its parametrised superclass
我正在使用MVP架构实现一个简单的应用程序。
这里是我的MvpView
和MvpPresenter
接口(没有什么有趣的MvpModel
,所以我跳过吧):
/// MvpView.java
public interface MvpView {
}
/// MvpPresenter.java
public interface MvpPresenter<V extends MvpView> {
void attachView(V view);
void detachView();
}
现在我有一个基本的MvpView
实现,这是一个Activity
:
// BaseActivity.java
public abstract class BaseActivity<V extends MvpView, P extends MvpPresenter<V>>
extends AppCompatActivity implements MvpView {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPresenter().attachView(this);
}
public abstract P getPresenter();
// other logic
}
至于我,一切看起来都正确,但是有一个编译错误:
getPresenter().attachView(this);
如果我将演员表添加到V
然后项目编译,一切正常:
getPresenter().attachView((V) this);
(1已经由伊兰回答)
BaseActivity
V
链接到BaseActivity
或如何更好地实现此MVP方法? 对我来说很奇怪,因为我的BaseActivity
正在扩展MvpView
因为它是由这个通用参数定义的: V extends MvpView
!
getPresenter()
是P
类型的实例,它扩展了MvpPresenter<V>
。 因此, getPresenter.attachView()
需要一个V
类型的参数。
现在,我们知道V
必须实现MvpView
,我们也知道BaseActivity
实现了MvpView
,但是这个实现不一定匹配。
例如,您可以创建一个具体的子类SubBaseActivity
并使用以下实例化它:
SubBaseActivity<MvpViewImpl, MvpPresenterImpl<MvpViewImpl>>
activity = new SubBaseActivity<> (); // let's ignore the fact that you are not suppose
// to instantiate Android activities this way
现在getPresenter()
返回一个MvpPresenterImpl
和getPresenter().attachView()
需要一个MvpViewImpl
类型的参数。 但是, this
并不类型MvpViewImpl
。
当您从BaseActivity<V,P>
到V
进行不安全的BaseActivity<V,P>
,您告诉编译器BaseActivity<V,P>
可以转换为V
但是,这在运行时工作的原因是编译器擦除了泛型类型参数V
和P
由于V
的类型绑定是MvpView
,因此对V
的转换成为MvpView
, BaseActivity
实现了它。
问题是你假设V
引用了BaseActivity<V,?>
因为你可能只是在实践中使用它。
不幸的是,目前没有办法引用你在类型参数边界中声明的类来强制这样的约束。
最简单的解决方法是使用未经检查的强制转换(如果您愿意,可以屏蔽警告):
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@SupressWarning("unchecked")
final V myView = (V) this;
getPresenter().attachView(myView);
}
您不需要使用本地myView
变量,但明确声明它只允许您静音该方法而不是任何其他“未经检查”的警告。
在任何情况下,您必须确保任何扩展类都将其V
类型参数设置为自己,以便不破坏合同:
public class ConcreteActivity<V extends MvpView, P extends MvpPresenter<V>> extends BaseActivity<ConcreteActivity<V,P>, P> {
...
}
您无法在编译时强制执行此操作,而是您的测试代码应使用反射来验证每个扩展类是否符合此类限制。
您可以更进一步,以避免未经检查的强制转换警告,但这需要您在BasicActivity构造函数中添加一个字段类型V
指向this
集合,该构造函数由扩展类构造函数作为参数传递....
public abstract class BaseActivity<V extends MvpView, P extends MvpPresenter<V>>
extends AppCompatActivity implements MvpView {
private final V myView;
protected BaseActivity(final V myView) {
if (myView != this) { throw new IllegalArgumentException("you must pass this object"); }
this.myView = myView;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPresenter().attachView(myView);
}
public abstract P getPresenter();
// other logic
}
public class ConcreteActivity<V extends MvpView, P extends MvpPresenter<V> extends BaseActivity<BaseActivity<V, P>, P> {
public ConcreteActivity() {
super(this);
}
...
}
请注意,我们仔细检查myView
是,其实this
在构造函数,以便如果扩展的类不符合早期失效; 如果您的测试代码确保始终如此,您可以将其保留。
尽管如此,最好避免任何形式的警告...我会说在这种情况下,第一种选择是完全可以接受的,因为它需要更少的代码并且在内存方面更有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.