[英]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.