泛型的深入理解和案例解析(3)--- T 和 ? 的区别

0. 引言

   在上一节《泛型的深入理解和案例解析(2)—上下界和通配符》我们提到了通配符“?”,他和T的语法貌似一样,也可以指定上下界,也可以用在集合类中,那么自然而言有个疑问,他们两个是什么关系,什么区别,各自用在什么情况下?
   我先放个结论:

  1. T 本质上是参数类型的声明,相当于形参,作用在定义阶段。
  2. “?” 本质上是参数类型的指定,和 Integer,String 等一样,相当于实参,作用在使用阶段。之所以叫通配符,就是表示某类特定的不确定类型

1. 顾名而思义

   如下图所示,来自Effective Java:

E/T : 形式类型参数
?:无限制通配符类型,本质上也是类型

所以,从名字上我们就能够得出,? 就是一种类型。

2. 代码验证T和?

  1. 定义方法,情况1
       可以看到定义方法的时候,由于我们的参数使用ArrayList指定,所以对于ArrayList里的泛型是使用阶段,必须指明。结果如下:
	private void AnimalRun(ArrayList<? extends Animal> animals) {
		//通过 ? 已经代表具体类型
        animals.get(0).run();
    }
    
    private <T> void AnimalTRun(ArrayList<T extends Animal> animals) {
        //错误,ArrayList要求传入具体类型,所以不行
    }
  1. 定义方法,情况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出现问题

  1. 提示“Unchecked call to ‘observe(LifecycleOwner, Observer<? super T>)’ as a member of raw type ‘androidx.lifecycle.LiveData’ ”。
  2. 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值