zoukankan      html  css  js  c++  java
  • Java基础之泛型的使用

    为什么要使用泛型

    首先我们先来看一段演示代码,如下所示,

    1 public static void main(String[] args) {
    2         List list = new ArrayList();
    3         list.add("abc");
    4         list.add(123);
    5         list.add(HashMap.class);
    6 
    7         System.out.println(list.get(0));
    8 }

    第2行,List里面的内容类型是Object类型,因此第3、4、5行可以接受String、Integer或者Class类型。然鹅,会存在两个严重的问题:

    ①在List集合中,增加元素时,集合不会记住元素的具体类型,对象的编译类型为Object类型,但是运行时类型仍然是本身的类型,如String等。

    ②在List集合中,取出元素时,需要对Object类型,进行转换,因为没有存储元素的真实类型,所以很容易出现ClassCastException异常。

    因此我们的需求需要满足,第一编译能够识别元素的类型,第二不能出现类型强转出现的异常。于是泛型就出现了。于是出现了开发者很熟悉的一段代码:

    在编译期间确定类型,只要不满足这个类型,则编译不通过。总结一下使用泛型带来的好处

    1.类型确定,使得如果出现类型不一致在编译期间不同通过,而不是在运行期间抛出异常。

    2.编码在逻辑代码中出现过多的强转,代码优雅性较好。

    我们再来看一段如下代码,

     1 public static void main(String[] args) {
     2         List<String> a = new ArrayList();
     3         List<Integer> b = new ArrayList<>();
     4 
     5         Class<? extends List> aClass = a.getClass();
     6         Class<? extends List> bClass = b.getClass();
     7 
     8         System.out.println(aClass +" &&  "+ bClass);
     9         System.out.println(aClass == bClass);
    10 }

    打印出来的结果是   class java.util.ArrayList   &&  class java.util.ArrayList    true。因此,泛型的类型不会对集合实例时什么类型造成影响。如上图所示的泛型类型分别为String和Integer,但是getClass类型确是相同,都为ArrayList。因此想要定义两个重载函数,如果使用形参列表来区分重载,仅仅通过泛型不同时不可行的,演示代码如下所示,编译期间不通过。

    泛型接口,泛型类,泛型方法

            泛型,指的是“参数化”类型,什么是参数化类型呢?有点类似方法中的形参和实参,形参是方法中形式参数之意,比较抽象的概念,而实参是实际参数之意,拥有具体的数值,比较具体的概念。而参数化类型,表明在定义时形式化,在使用时具体化。下面看一下List接口的定义。

    public interface List<E> extends Collection<E>

    E就是参数化的类型,也就是泛型。这个接口叫作泛型接口,我们再看一下ArrayList的类定义;

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable

    同样的这个类叫作泛型类,再来看泛型类中的泛型方法。如下所示,

     1 public boolean add(E e) {
     2         ensureCapacityInternal(size + 1);  // Increments modCount!!
     3         elementData[size++] = e;
     4         return true;
     5 }
     6 
     7 public E remove(int index) {
     8         rangeCheck(index);
     9 
    10         modCount++;
    11         E oldValue = elementData(index);
    12 
    13         int numMoved = size - index - 1;
    14         if (numMoved > 0)
    15             System.arraycopy(elementData, index+1, elementData, index,
    16                              numMoved);
    17         elementData[--size] = null; // clear to let GC do its work
    18 
    19         return oldValue;
    20     }
    21 
    22 
    23 public E get(int index) {
    24         rangeCheck(index);
    25 
    26         return elementData(index);
    27 }

    以上代码是ArrayList泛型类中的 add,remove,get。add方法的元素类型是E,remove方法的返回值类型也是E,get方法的返回值类型也是E。这个E就是最先我们接口中的定义的泛型接口参数E。   这就解释了为什么 在List<Integer> 中add("abc")会编译不通过,也解释了为什么在List<Integer>中 get(0)的返回值会是 Integer。

    类型通配符

    Object类是List的超类,然而List<Object >不是List<String>的父类,Number是Integer的抽象父类。然后List<Number>不是List<Integer>的父类。

    因此,List<Integer>不能向上转型。此时就需要引入一个叫类型通配符的东西。类型通配符使用  ?  类表示。通配符“?”表示所有泛型的父类型。如下所示,

    1 public List<?> filter(List<?> list) {
    2 
    3 
    4         
    5         return list;
    6 }
    List<String> aa = new ArrayList();
    List<String> filter = (List<String>)filter(aa);
    在客户端可以直接调用filter方法,返回值依然是List类型,泛型为?即所有泛型的父类型,因此使用List<String>强转即可。使用类型通配符只能查询过滤或者统计元素,不能进行新增等操作,因为类型是抽象的,新增需要明确的类型。这里要提一下,我们

     这里需要指出,我们在应用时经常要对  ?做出约束, 比如现在上述的  filter方法中,的形参,只能传递  Integer的父类。那代码就变成了:

    public static List<?> filter(List<? super Integer> list) {
            return list;
    }

    ps:Integer和Integer的父类Number都是可的,List<Integer>、List<Number> 等。客户端调用如下所示。

    List<Number> aa = new ArrayList();

    List<Number> filter = (List<Number>)filter(aa);

    和<? super Integer>相对的是<? extends Number>表示形参中的类型只能是Number类型或者是他的子类。

    1 public static List<?> filter(List<? extends Number> list) {
    2 
    3         return list;
    4 }

    最后强调一下

    1.在新增一个泛型类时候,在类后面待上泛型,这个泛型不能是通配符“?”,而且如果要表示继承关系只能使用<T extends  父类>的形式,不能使用<T super 某个类>。

    2.静态资源不识别泛型。静态变量,静态方法,静态方法块,静态类不识别泛型。

  • 相关阅读:
    Choosing the Type at Runtime
    User-Defined Components Must Be Capitalized
    Computed property names
    Controlled Components
    Handling Event
    State
    props
    Functional and Class Components
    招聘漂亮的员工
    Spread Syntax
  • 原文地址:https://www.cnblogs.com/sunshine798798/p/9749964.html
Copyright © 2011-2022 走看看