欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java泛型概述

程序员文章站 2024-03-23 13:16:34
...

泛型概述

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

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

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

 

为什么要有泛型(Generic)?

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

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

Java泛型概述

当我们将一个元素放入集合中,集合不会记住此元素的类型,当再次从集合中取出此元素时,该元素的编译类型变成了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)从泛型类派生子类,泛型类型需具体化