1、 泛型为JDK1.5 之后出现的新特性,类中的属性类型由外部定义,而且在声明类的时候应该采取下面形式:
class 类名称 < 泛型类型,泛型类型, ...>{
}
如下类Point :
package com.chenzehe.test;
public class Point<T> { // T为泛型类型,也可以由其它字母声明
private T x ;
private T y ;
public T getX() {
return x ;
}
public void setX(T x) {
this . x = x;
}
public T getY() {
return y ;
}
public void setY(T y) {
this . y = y;
}
}
程序中属性的类型不再由程序中设定,而是由实例化对象的时候在外部指定。
在没有泛型之前,从集合中读取到每个对象都必须进行转换,如果不小心插入了类型错误的对象,只有在运行到转换的地方才会出现错误。有了泛型之后,可以告诉编译器每个集合中接受哪些对象类型,在编译的时候就能知道插入的类型是否正确。
2、 泛型擦除
如果在使用泛型的时候没有指定泛型类型,则表示擦除泛型,class<T> 将按照 Object 类型进行接收,保证程序不出错,如下:
package com.chenzehe.test;
public class Test {
public static void main(String[] args) {
Point point = new Point ();
point.setX(10) ;
point.setY(20) ;
}
}
但是该程序会出现警告信息,要想去掉警告信息,则指定 T为 Object 类型,如
Point <Object> point = new Point <Object>();
但是这种程序没有任何意义,在开发中建议不要擦除泛型。
每全泛型都定义一个原生态类型(raw type),即不带任何实际类型参数的泛型名称,如List<T>相对应的原生态类型就是List,原生态类型就像从类型声明中删除所有泛型信息一样。
在jdk 1.5之前,有如下代码:
private final Collection stamps=... ;
如果不小心将一个Coin类型对象放到stamp集合中,这种错误在编译时不会出现任何错误:
stamps.add(new Coin(...));
for (Iterator i = stamps.iterator() ; i.hasNext();){
Stamp s = (Stamp) i.next();
}
private final Collection<Stamp> stamps = ... ;
如果插入的对象类型不是Stamp编译器就会报错而不用等到运行到该行代码才出错。
如上所述,如果不提供类型参数,使用集合类型和其它泛型也仍然是合法的,但是不应该这么做,如果使用原生态类型,就失掉了泛型在安全性和表述性方面的优势。既然不应该这么做,但是为什么jdk中还允许呢,这是为了提供兼容性,因为泛型出现的时候,已经存在没有使用泛型的大量java代码,人们认为让所有这些代码保持合法,并且能够与使用泛型的代码互用。
3、 泛型通配符?
如下程序:
Integer integer = new Integer(10);
Object obj = integer;
可以进行向上转型,而对于泛型中定义的类型则不能向上转型,如:
Point<Object> objectPoint = new Point<Object>();
Point<Integer> integerPoint = new Point<Integer>();
objectPoint = integerPoint ; // 不能向上转型
如下代码,在调用 print函数时不能向上转型而出错:
package com.chenzehe.test;
public class Test {
public static void main(String[] args) {
Point<Object> objectPoint = new Point<Object>();
Point<Integer> integerPoint = new Point<Integer>();
print (objectPoint);
print (integerPoint); //不能向上转型,调用出错
}
public static void print(Point<Object> p) {
System. out .println(p.getX());
System. out .println(p.getY());
}
}
要想实现调用,则可以在声明函数参数时使用擦除泛型的方式,如下:
public static void print(Point p) {
System. out .println(p.getX());
System. out .println(p.getY());
}
这样 print (integerPoint); 就可以实现调用,但是 print函数声明还是有警告信息,并且在使用时转型还可能出现异常,最好的解决方法是声明 print 函数时使用泛型通配符?,如:
public static void print(Point< ? > p) {
System. out .println(p.getX());
System. out .println(p.getY());
}
?表示可以接收任何泛型,但是使用泛型通配符时,该类型对象只有可读属性,没有可写属性,如上面p.getX() 方法可以使用,但是 set 方法不能使用,如 p. setX (123); 不可以使用。
泛型通配符和原生态类型间有什么区别?泛型通配符是类型安全的,原生态类型是不安全的。
虽然不应该在代码中使用像List这样的原生态类型,使用参数化的类型以允许插入任意对象,如List<Object>,这样还是可以的。不严格的来说,原生态类型逃避了类型检察,而List<Object>告诉编译器他能够持有任意类型的对象。虽然你可以将List<String>传递给类型List的参数,但是不能将他传递给类型List<Object>的参数。
4、 泛型上限
指泛型类型可以指定的最大的父类,如将上限设置为Number 类型,则此时可以接收的泛型类型只能为 Number 或其子类 Integer 等,语法为:
Class <T extends 父类 >
如将上面Point 类泛型设置为最后只能是 Number 类型:
package com.chenzehe.test;
public class Point< T extends Number> { // T 只能为 Number类型或其子类
private T x ;
private T y ;
public T getX() {
return x ;
}
public void setX( T x) {
this . x = x;
}
public T getY() {
return y ;
}
public void setY( T y) {
this . y = y;
}
}
在使用该泛型时, Point<Integer> integerPoint = new Point<Integer>(); 可以使用,而 Point< String > stringPoint = new Point< String >(); 则不能使用。
泛型上限也可以在方法上使用,如下面在接收参数时使用:
package com.chenzehe.test;
public class Test {
public static void main(String[] args) {
Point<Integer> integerPoint = new Point<Integer>();
print (integerPoint);
}
public static void print(Point<? extends Number > p) {
System. out .println(p.getX());
System. out .println(p.getY());
}
}
5、 泛型下限
指泛型只能设置其具体的类或者父类,使用super 关键字定义,在定义泛型类型时不能使用泛型下限,只有在使用时才能定义下限,如:
Point类要定义成 public class Point<T super Integer> 则报错,但是在 print方法中可以定义,如:
public static void print(Point<? super Integer > p) {
System. out .println(p.getX());
System. out .println(p.getY());
}
6、 泛型接口
泛型不但可以作用在类上,还可以作用在接口上,定义如下:
interface 接口名称 < 泛型类型 , 泛型类型 ,...>
如下实例定义泛型接口IDemo :
package com.chenzehe.test;
public interface IDemo<T> {
void print ( T param ) ;
}
有两种方式可以实现该接口,方式一:实现类不指定具体泛型类型
package com.chenzehe.test;
public class DemoImpl<T> implements IDemo<T> {
public void print ( T param ) {
System. out .println ( "param = " + param ) ;
}
}
方式二:实现类指定具体泛型类型
package com.chenzehe.test;
public class DemoImpl implements IDemo< String > {
public void print ( String param ) {
System. out .println ( "param = " + param ) ;
}
}
7、 泛型方法
泛型还可以定义在方法上,在方法中使用泛型,该方法所在的类不一定是泛型操作类。如:
package com.chenzehe.test;
public class Demo {
public <T> T print ( T param ) {
return param ;
}
}
也可以将方法返回值定义成泛型数组:
public <T> T [] Print ( T... params ) {
return params;
}
8、 泛型嵌套设置
定义Info 类:
package com.chenzehe.test;
public class Info<T> {
private T param ;
public T getParam () {
return this . param ;
}
public void setParam ( T param ) {
this . param = param;
}
}
定义Person 类:
package com.chenzehe.test;
public class Person< T > {
private T info ;
public T getInfo () {
return this . info ;
}
public void setInfo ( T info ) {
this . info = info;
}
}
测试类:
package com.chenzehe.test;
public class Test {
public static void main ( String [] args ) {
Person<Info<String>> person = new Person<Info<String>> () ;
person.setInfo ( new Info<String> ()) ;
person.getInfo () .setParam ( "chenzehe" ) ;
System. out .println ( person.getInfo () .getParam ()) ;
}
}
9、 消除非受检警告
在使用泛型编程时,会遇到许多编译器警告:非受检强制转化警告(unchecked cast warnings)、非受检方法调用警告、以及非受检转换警告(unchecked conversion warnings)。
有许多非受检警告很容易消除,如下面的警告:
Set<Lark> exaltation = new HashSet();
Set<Lark> exaltation = new HashSet<Lark>();
10、列表优先于数组
数组与泛型相比有两个重要的不同点,首先,数组是协变的(covariant),表示如果Sub为Super的子类型,那么数组类型Sub[]就是Super[]的子类型,相反泛型是不可变的(invariant),表示对于任意两个不同的类型Type1和Type2,List<Type1>即不是List<Type2>的子类型,也不是List<Type2>的父类型。
下在代码是合法的:
// Fails at runtime
Object[] objectsArray = new Long[1];
objectsArray[0] = "I don't fit in"; // Throws ArrayStoreException
// Won't compile!
List<Object> ol = new ArrayList<Long>(); // Incompatible types
ol.add("I don't fit in");
import java.util.Arrays;
import java.util.EmptyStackException;
/**
* Huisou.com Inc. Copyright (c) 2011-2012 All Rights Reserved.
*/
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (0 == size) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;// 不设置为null可能导致内存溢出
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
import java.util.Arrays;
import java.util.EmptyStackException;
/**
* Huisou.com Inc. Copyright (c) 2011-2012 All Rights Reserved.
*/
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new E[DEFAULT_INITIAL_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if (0 == size) {
throw new EmptyStackException();
}
E result = elements[--size];
elements[size] = null;// 不设置为null可能导致内存溢出
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
elements = new E[DEFAULT_INITIAL_CAPACITY];
elements =(E[]) new Object[DEFAULT_INITIAL_CAPACITY];
@SuppressWarnings("unchecked")
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
private Object[] elements;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
E result = elements[--size];
E result = (E) elements[--size];
public E pop() {
if (0 == size) {
throw new EmptyStackException();
}
@SuppressWarnings("unchecked")
E result = (E) elements[--size];
elements[size] = null;// 不设置为null可能导致内存溢出
return result;
}