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)从泛型类派生子类,泛型类型需具体化
上一篇: tensflow实现手写识别代码
下一篇: 优化Swift的构建时间