Java泛型概述

泛型概述

Java 泛型(Generic)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

把一个集合中的内容限制为一个特定的数据类型,这就是泛型背后的核心思想。

 

为什么要有泛型(Generic)?

1)解决元素存储的安全性问题

2)解决获取数据元素时,需要类型强转的问题

当我们将一个元素放入集合中,集合不会记住此元素的类型,当再次从集合中取出此元素时,该元素的编译类型变成了Object类型,但其运行时类型仍然为其本身类型。我们可以通过以下的例子来说明:

public class GenericDemo {
    public static void main(String[] args) {
        List list = new ArrayList(); // 创建无泛型的集合,此时list默认的类型为Object类型
        list.add("zhaowajun"); // 先添加一个字符串型元素
        list.add(10); // 再添加整形元素
        for (int i = 0; i < list.size(); i++) {
            // 取出集合元素时需要人为的强制类型转化到具体的目标类型
            String element= (String) list.get(i); 
            System.out.println("element:" + element);
        }
    }
}

以上示例在编译阶段是正常的,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。这时候如果使用泛型就可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常。

泛型的本质是参数化类型,提到参数我们最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

public class GenericDemo {
    public static void main(String[] args) {
        // 通过List<String>,直接限定了list集合中只能含有String类型的元素
        List<String> list = new ArrayList<String>();
        list.add("zhaowajun"); 
        list.add("generic");
        // list.add(10); // 想加入一个Integer类型的对象时会出现编译错误
        for (int i = 0; i < list.size(); i++) {
            // 无须进行强制类型转换,此时集合能记住元素类型信息,编译器已能够确认它是String类型了
            String element= list.get(i); 
            System.out.println("element:" + element);
        }
    }
}

结合上面的泛型定义,我们知道在List<String>中,String是类型实参,也就是说,相应的List接口中肯定含有类型形参。且get()方法的返回结果也直接是此形参类型(也就是对应的传入的类型实参)。下面来看看List接口的的具体定义:

public interface List<E> extends Collection<E> {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();
    <T> T[] toArray(T[] a);
    boolean add(E e);
    boolean remove(Object o);
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean addAll(int index, Collection<? extends E> c);
    boolean removeAll(Collection<?> c);
    boolean retainAll(Collection<?> c);
    void clear();
    boolean equals(Object o);
    int hashCode();
    E get(int index);
    E set(int index, E element);
    void add(int index, E element);
    E remove(int index);
    int indexOf(Object o);
    int lastIndexOf(Object o);
    ListIterator<E> listIterator();
    ListIterator<E> listIterator(int index);
    List<E> subList(int fromIndex, int toIndex);
}

我们可以看到,在List接口中采用泛型化定义之后,<E>中的E表示类型形参,可以接收具体的类型实参,并且此接口定义中,凡是出现E的地方均表示相同的接受自外部的类型实参。自然的,ArrayList作为List接口的实现类,其定义形式是:

public class ArrayList<E> extends AbstractList<E> 
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }   
    public E get(int index) {
        rangeCheck(index);
        checkForComodification();
        return ArrayList.this.elementData(offset + index);
    }    
    //...省略掉其他具体的定义过程
}

由此,可以解释清楚为什么List<String>类型的集合list中加入Integer类型对象会编译错误,且遍历list元素get()到的类型直接就是String类型了。

 

什么时候使用泛型?

当接口、类及方法中的操作的引用数据类型不确定的时候,以前用的Object来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦,而且将运行问题转移到的编译时期。

 

Java类库中的泛型有哪些?

所有的标准集合接口都是泛型化的—— Collection<V>、List<V>、Set<V> 和 Map<K,V>。类似地,集合接口的实现都是用相同类型参数泛型化的,所以HashMap<K,V> 实现 Map<K,V> 等。

除了集合类之外,Java 类库中还有几个其他的类也充当值的容器。这些类包括 WeakReference、SoftReference 和 ThreadLocal。

 

注意:    

1)对象实例化时不指定泛型,默认为:Object。

2)泛型不同的引用不能相互赋值。

3)加入集合中的对象类型必须与指定的泛型类型一致。

4)静态方法中不能使用类的泛型。

5)如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。

6)不能在catch中使用泛型

7)从泛型类派生子类,泛型类型需具体化

猜你喜欢

转载自blog.csdn.net/lxxiang1/article/details/81293209