泛型学习
一.Java中引入泛型(Generics)的原因:
1.解决数据类型转换的错误:
由于在程序中要使用具体的数据类型,因此要对表达式进行向下的类型转换(downCast),
编译时会通过,但在运行时会发生ClassCastException异常。
类型转换的缺点:
1.使java代码变得更加冗长
2.降低了静态类型检查的价值(因为每个数据类型转换都是一个选择忽略静态类型检查的伪指令)。
二.自定义泛型:
1.定义泛型类:
//GenericFoo.java
public class GenericFoo<T>
{ private T foo;
public void setFoo(T foo){
this.foo = foo;
}
public T getFoo(){
return foo;
}
}
使用<T>来声明一个类型持有者(Holder)名称T,用T这个名称作为类型代表来声明成员,参数,或返回值类型。
2.使用自定的泛型:
//GenericFooDemo.java
public class GenericFooDemo{
public static void main(String [] args){
GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
foo1.setFoo(new Boolean(true));
Boolean b = foo1.getFoo();//不需要再转换类型
System.out.println(b);
foo2.setFoo(new Integer(10));
Integer i = foo2.getFoo();//不需要再转换类型
System.out.println(i);
}
}
3.与使用Object声明类型不同的地方:
使用泛型所定义的类在声明及配置对象时,可以使用<Type>指定类型持有者T真正的类型,就不需要进行类型的强制转换();
getFoo()设置的参数和返回的德类型,就是声明在<>之间所指定的类型。这样就避免了ClassCastException异常的发生。
4.如在定义泛型类时,不指定类型,默认使用Object类型。
5.自定义泛型类时,类型持有者名称可以使用T(Type);
如果是容器的元素,使用E(Element);
若键值匹配(hash表)可以使用K(key)与V(Value);
若是Annotation可以使用A。
三。泛型的高级语法:
1.限制泛型的可用类型:
在定义类型的持有者时,使用extends指定指定这个类型持有者的实例化,实例化对象必须是扩充自某个类型或实现某个借口
//ListGenericFoo.java
import java.util.List;
public class ListGenericFoo<T extends List>{
private T[] fooArray;
public void setFooArray(T[] fooArray){
this.fooArray = fooArray;
}
public T[] getFooArray(){
return fooArray;
}
}
ListGenericFoo在声明类型持有者时,一并指定这个持有者实例化的对象,必须是实现java.ytil.List接口的类。
使用extends限制实例化生成的对象,必须是实现List接口的类。如LinkedList,ArrayList;
2.类型通配符(wildcard)
如果你想有一个foo名称可以参考的对象,其类型持有者实例化的对象是实现List接口的类或者子类,可以使用?(通配符)。
?代表未知类型,并使用extends关键词做限定。eg:
GenericFoo<? extends List> foo = null;
foo1 = new GenericFoo<ArrayList>();
...
foo2 = new GenericFoo<LinkedList>();
...
如果实例化的对象不是实现List接口的类,则编译器会报告错误。eg:
GenericFoo<? extends List> foo = new GenericFoo<HashMap>();
编译器会报如下的错误:
incompatible types
found : GenericFoo<java.util.HashMap>
required:GenericFoo<? extends java.util.List>
GenericFoo<> foo = new GenericFoo<HashMap>();
因为HashMap没有实现List接口,所以建立的GenericFoo<HashMap>实例不能指定给foo名称来参考。
注意:通过使用通配符声明的名称所参考的对象,你没办法再对它加入新的信息,你只能取得或者移除它当中的信息。
除了可以向下限制,也可以向上限制,使用super实现.eg:
GenericFoo<? super StringBuilder> foo = null;
...
扩展一个泛型类
//GenericFoo4.java
public class GenericFoo4<T1,T2>{
private T1 foo1;
private T2 foo2;
public void setFoo1(T1 foo1){
this.foo1 = foo1;
}
public T1 getFoo1(){
return foo1;
}
public void setFoo2(T2 foo2){
this.foo2 = foo1;
}
public T2 getFoo2(){
return foo1;
}
}
a.如果决定要保留类型持有者,则在子类中必须写齐全
//Sub1GenericFoo4.java
public class SubGenericFoo4<T1,T2> extends GenericFoo4<T1,T2>{
private T3 foo3;
public void setFoo1(T1 foo1){
this.foo1 = foo1;
}
public T1 getFoo1(){
return foo1;
}
public void setFoo2(T2 foo2){
this.foo2 = foo2;
}
public T2 getFoo2(){
return foo2;
}
public void setFoo3(T3 foo3){
this.foo3 = foo3;
}
public T3 getFoo3(){
return foo3;
}
}
b.如果不保留类型持者,继承下来的T1和T2自动转换成Object。
//Sub2GenericFoo4.java
public class SubGenericFoo4<T1,T2> extends GenericFoo4<T1,T2>{
private T3 foo3;
public void setFoo3(T3 foo3){
this.foo3 = foo3;
}
public T3 getFoo3(){
return foo3;
}
}
3.泛型的本质就是:
将<font color="#8b0000"></font>所操作的数据类型参数化,也就是说,该数据类型被指定为一个参数。
4.使用泛型的好处在于:
它在编译的时候进行类型安全检查,并且在运行时所有的转换都是强制的,隐式的,大大提高了代码的重用率。
结束语:因为在看JDK源码时看到大量的泛型使用,所以才找了些基础的泛型介绍,希望网友能举些复杂的泛型示例进行分析,欢迎讨论...
参考资料:《java学习笔记》 林信良著;
http://blog.csdn.net/bluesmile979/archive/2009/03/10/3976905.aspx
http://www.ibm.com/developerworks/cn/java/j-djc02113/