0. 引言
在上一节《泛型的深入理解和案例解析(2)—上下界和通配符》我们提到了通配符“?”,他和T的语法貌似一样,也可以指定上下界,也可以用在集合类中,那么自然而言有个疑问,他们两个是什么关系,什么区别,各自用在什么情况下?
我先放个结论:
- T 本质上是参数类型的声明,相当于形参,作用在定义阶段。
- “?” 本质上是参数类型的指定,和 Integer,String 等一样,相当于实参,作用在使用阶段。之所以叫通配符,就是表示某类特定的不确定类型。
1. 顾名而思义
如下图所示,来自Effective Java:
E/T : 形式类型参数
?:无限制通配符类型,本质上也是类型。
所以,从名字上我们就能够得出,? 就是一种类型。
2. 代码验证T和?
- 定义方法,情况1
可以看到定义方法的时候,由于我们的参数使用ArrayList指定,所以对于ArrayList里的泛型是使用阶段,必须指明。结果如下:
private void AnimalRun(ArrayList<? extends Animal> animals) {
//通过 ? 已经代表具体类型
animals.get(0).run();
}
private <T> void AnimalTRun(ArrayList<T extends Animal> animals) {
//错误,ArrayList要求传入具体类型,所以不行
}
- 定义方法,情况2
这时候是真正的泛型方法的定义,?是具体指明类型,所以不能作为参数类型表示。
private void AnimalRun(? animals) {
//定义阶段,编译错误
}
private <T extends Animal> void AnimalTRun(T animals) {
//泛型定义阶段,编译通过
}
3. 一个典型的泛型嵌套问题
MVVM架构中,BaseFragment与BaseViewModel泛型绑定,BaseViewModel又与BaseModel泛型绑定。在BaseFragment中,调用泛型BaseViewModel时,由于类型未指定,导致获取的LiveData成员变量,observer不能够自动识别出类型。
代码示例如下:
public class BaseModel {
private static final String TAG = BaseModel.class.getCanonicalName();
public BaseModel() {
Log.d(TAG, "init constructor");
}
}
//BaseViewModel通过泛型绑定Model,到真正的代码实现阶段,再指定具体的Model实体
public class BaseViewModel<T extends BaseModel> extends ViewModel {
public static final String TAG = BaseViewModel.class.getCanonicalName();
public T model;
public BaseViewModel() {
ReflexUtils<T> reflexUtils = new ReflexUtils<>();
model = reflexUtils.getObj(this.getClass());
}
MutableLiveData<String> testMsg = new MutableLiveData<>();
public MutableLiveData<String> getTestMsg() {
return testMsg;
}
public void outPut() {
Log.d(TAG, "outPut: ");
}
public Integer getInt() {
return 1;
}
}
//同理BaseFragment也是动态绑定BaseViewModel,到真正的Fragment实现时,确定具体类型。
public class BaseFragment<T extends BaseViewModel> extends Fragment {
private final static String TAG = FLAG + BaseFragment.class.getSimpleName();
public T sViewModel;
public BaseFragment() {
Log.d(TAG, "init constructor");
}
private void initLive() {
sViewModel.outPut();
int x = sViewModel.getInt();
sViewModel.getTestMsg().observe(getViewLifecycleOwner(), new Observer() {
@Override
public void onChanged(Object o) {
}
});
}
public Class<T> getTClass(){
ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
return (Class)type.getActualTypeArguments()[0];
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ReflexUtils<T> reflexUtils = new ReflexUtils<>();
sViewModel = new ViewModelProvider(this).get(getTClass());
return super.onCreateView(inflater, container, savedInstanceState);
}
}
问题:sViewModel.getTestMsg().observe出现问题:
- 提示“Unchecked call to ‘observe(LifecycleOwner, Observer<? super T>)’ as a member of raw type ‘androidx.lifecycle.LiveData’ ”。
- observe方法无法自动识别出getTestMsg是String,从而默认使用了object。
问题分析:我们知道这个是BaseViewModel并未指定具体绑定的Model类型导致的,但是我们并不想在定义BaseFragment就把BaseViewModel绑定的Model指定完成,怎么办?
这时候就用到了通配符 “?”,解决又要确定类型,但是又不明确是哪种类型的情况。
代码修改如下,完美解决了上述问题:
public class BaseFragment<T extends BaseViewModel<? extends BaseModel>> extends Fragment {
private final static String TAG = FLAG + BaseFragment.class.getSimpleName();
public T sViewModel;
public BaseFragment() {
Log.d(TAG, "init constructor");
}
private void initLive() {
sViewModel.outPut();
int x = sViewModel.getInt();
sViewModel.getTestMsg().observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String s) {
//能够自动识别类型了。
}
});
}
public Class<T> getTClass(){
ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
return (Class)type.getActualTypeArguments()[0];
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ReflexUtils<T> reflexUtils = new ReflexUtils<>();
sViewModel = new ViewModelProvider(this).get(getTClass());
return super.onCreateView(inflater, container, savedInstanceState);
}
}