概述

java的泛型是在java1.5才引入的,也就是说不是天生的,是后天加入的,为了兼容之前的代码,java采用了一种擦除的方式实现了泛型。也就是因为擦除的原因,所以java的泛型使用起来很别扭。
本来泛型的出现,是为了让我们的代码更加泛化一些,但是java的泛型不够泛化。
如果是用不变的类型参数T定义了泛型类或者泛型方法,在泛型代码里是不能用泛型参数调用方法的,当然Object的方法除外,类型参数被擦除到了Object,也就是说泛型代码里不知道类型参数代表的具体类型的。也就是说泛化了,可是功能受限了。
这个问题在C++中是不存在的,T可以调用你设计的方法,这个思想有个专业概念是潜在的类型机制(Latent typing)或者结构化的类型机制,还有个更通俗和流程的叫法是鸭子类型机制
为了解决上面的问题,java中重载了extends关键字,让泛型类型参数extends具体的类或者接口,这样泛型代码中就可以通过类型参数T调用具体类或者接口中方法。但是这么做的话,就让泛化的概念打折扣了,不那么泛化了,传入类型必须是特定类或其子类。有时候,这种情况的话,就没必要使用泛型了,直接用接口就可以了。

泛型从更高的层次来看,其实是实现了类或者方法和其中使用类型的解耦。这就就能做到泛化

擦除

在《Think in java》中一句对擦除的描述:“在泛型代码内部中,无法获得任何有关泛型参数类型的信息”。通过事实证明这个理解是错的,Gson TypeToken的实现就证明的,这句话是错的。
根据目前自己的有限认知,说一下自己的理解:如果是定义泛型类,泛型类中的泛型类型形参数是会被擦除的,擦除到第一边界;但是,如果是在定义的类的时候,父类泛型类传入了实参的话,我们在运行期还是能获取到泛型类型参数的。我们在理解擦除概念的时候,要小心,不是想当然的认为,所有泛型都运行期都消失了。

关于Gson的TypeToken【Java】java.lang.reflect.Type详解

特殊说明一下:中文表述不够准确,泛型类型这个词被用的很混乱,它有时表示C,因为类其实也可以表示一个类型,有时T也被叫做泛型类型,T其实是表示类型的一个变量。

原生类型和泛型类型

java为了做到向后兼容,泛型类型的变量是可以赋给原生类型变量的,比如List list = new ArrayList();
在实际开发中,也会遇到把原生类型变量赋给泛型类型变量,也是可以正常工作,而且很常见。

<K, V> Map<K, V> getMap() {
        Map map = new HashMap();
        return map;
    }


    //采用目标类型进行类型推断
    Map<String , Integer> map = this.getMap();

泛型在继承过程中的使用

java泛型,要使用好,还是挺难的,别说会用,有时看懂都费劲。没办法只能多阅读别人的代码,强化自己的认识。在阅读mosby的时候,就对泛型在继承中的使用有了新的认识。
直接看源码分析吧

public abstract class MvpFragment<V extends MvpView, P extends MvpPresenter<V>> extends Fragment
    implements MvpDelegateCallback<V, P>, MvpView {

  protected FragmentMvpDelegate<V, P> mvpDelegate;

  protected P presenter;

  public abstract P createPresenter();

  @NonNull protected FragmentMvpDelegate<V, P> getMvpDelegate() {
    if (mvpDelegate == null) {
      mvpDelegate = new FragmentMvpDelegateImpl<>(this, this, true, true);
    }

    return mvpDelegate;
  }

  @NonNull @Override public P getPresenter() {
    return presenter;
  }

  @Override public void setPresenter(@NonNull P presenter) {
    this.presenter = presenter;
  }

  @NonNull @Override public V getMvpView() {
    return (V) this;
  }

  @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    getMvpDelegate().onViewCreated(view, savedInstanceState);
  }

  @Override public void onDestroyView() {
    super.onDestroyView();
    getMvpDelegate().onDestroyView();
  }

........

}
public interface MvpPresenter<V extends MvpView> {
  @UiThread
  void attachView(@NonNull V view);
  @UiThread
  @Deprecated
  void detachView(boolean retainInstance);
  @UiThread
  void detachView();
  @UiThread
  void destroy();
}
public interface MvpDelegateCallback<V extends MvpView, P extends MvpPresenter<V>> {

  @NonNull P createPresenter();

  P getPresenter();

  void setPresenter(P presenter);

  V getMvpView();
}
public interface MvpView {
}

初学泛型,看到MvpFragment的定义是不是头大,好复杂呀!其实,多理解多训练强化还是可以理解的。
MvpFragment实现了泛型接口MvpDelegateCallback,并且继承了泛型类型形参V和P,首先看看V和P在MvpDelegateCallback中的定义,V extends MvpView,P extends MvpPresenter,也就是V是MvpView或者子类就可以,并且这个V是能满足MvpPresenter泛型参数定义的,P要是MvpPresenter或者子类。
编译器会进行检查的,如果V改成extends View,MvpPresenter的V就会报错Type parameter 'V' is not within its bound; should implement 'com.hannesdorfmann.mosby3.mvp.MvpView',也就是说,虽然不知道V到底是啥,但是得满足MvpPresenter泛型参数定义的边界。MvpDelegateCallback定义了V和P的边界。
MvpFragment继承了MvpDelegateCallback的V和P,在MvpFragment中V和P也是需要有边界的,并且只能比MvpDelegateCallback小或相等,要不然编译器检查后会报错。MvpFragment定义了一样的边界,后面会看到小边界的定义。

我们再看看MvpFragment的定义,MvpFragment还实现了MvpView。我一开始看到这样的定义,就很蒙圈。MvpFragment本身是个MvpView,泛型V定义说明MvpFragment里面用到了MvpView,其实如果是这样也没啥,只是V在MvpFragment的使用是getMvpView返回MvpFragment自己。这样做是,MvpView还有子接口的时候,在扩展MvpFragment定义新类的时候,能够知道更精确的MvpView。语言很难描述情况,看下面MvpLceFragment和MvpLceView的定义就明白了。

public abstract class MvpLceFragment<CV extends View, M, V extends MvpLceView<M>, P extends MvpPresenter<V>>
    extends MvpFragment<V, P> implements MvpLceView<M> {

  protected View loadingView;
  protected CV contentView;
  protected TextView errorView;

  @CallSuper @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    loadingView = createLoadingView(view);
    contentView = createContentView(view);
    errorView = createErrorView(view);
	....
 
  }

  @Override public void showLoading(boolean pullToRefresh) {

    if (!pullToRefresh) {
      animateLoadingViewIn();
    }

  @Override public void showContent() {
    animateContentViewIn();
  }

  @Override public void showError(Throwable e, boolean pullToRefresh) {

    String errorMsg = getErrorMessage(e, pullToRefresh);

    if (pullToRefresh) {
      showLightError(errorMsg);
    } else {
      errorView.setText(errorMsg);
      animateErrorViewIn();
    }
  }

  @Override public void onDestroyView() {
    super.onDestroyView();
    loadingView = null;
    contentView = null;
    errorView.setOnClickListener(null);
    errorView = null;
  }
}
public interface MvpLceView<M> extends MvpView {
  @UiThread
  void showLoading(boolean pullToRefresh);
  @UiThread
  void showContent();
  @UiThread
  void showError(Throwable e, boolean pullToRefresh);
  @UiThread
  void setData(M data);
  @UiThread
  void loadData(boolean pullToRefresh);
}

在这里先说一个编程技巧,MvpView定义了一个接口,MvpFragment实现了MvpView接口,后来发现MvpView定义的功能不够用,所以又基于MvpView扩展了一个接口MvpLceView,但是又不想破坏MvpFragment的代码,因为被其它地方多次使用,我们可以像MvpLceFragment一样,继承MvpFragment实现MvpLceView接口就好了。

MvpLceView继承MvpView,同时增加了泛型类型定义M。MvpLceFragment继承MvpFragment实现MvpLceView,因为MvpFragment和MvpLceView的泛型都没有实参化,所以在MvpLceFragment中还得列出并定义。M没有边界,什么类型都行;V边界变小了,变成了MvpLceView,这个编译器是可以接受的;P的边界有些变化,V的边界小了,也是能接受的;同时MvpLceFragment还添加了自己的泛型类型参数CV。
我理解,编译器在检查泛型的时候,只要满足边界就行,因为它也只知道边界信息。

下面看看MvpLceFragment是怎么用的

public class CountriesFragment
    extends MvpLceFragment<SwipeRefreshLayout, List<Country>, CountriesView, CountriesPresenter>
    implements CountriesView, SwipeRefreshLayout.OnRefreshListener {
}
public interface CountriesView extends MvpLceView<List<Country>> {
}
public interface CountriesPresenter extends MvpPresenter<CountriesView>{

  void loadCountries(final boolean pullToRefresh);
}

List泛型的转换

java vector泛型 java泛型怎么实现_ide


java vector泛型 java泛型怎么实现_ide_02


上面这样写会报错,如果这样写允许,那么就会导致能把C放到B的list中,实际运行取值发生类型转换的错误。为了避免这种错误,编译器直接禁止这样写代码

java vector泛型 java泛型怎么实现_ide_03


为了解决上面的问题,可以使用通配符的方式,但是得做出牺牲,禁止使用set方法,这样可以避免类型转换的错误。