java泛型
一、简介:
jdk1.5新推出的概念,c++中叫做STL
ClassCastException 类型转换异常,泛型多是解决这些问题。
使用泛型后,编译时候不会出问题,结束时候也不会出现问题。
package com.liuyao.generic;
classBooleanFool{
privateBoolean foo;
publicBoolean getFoo(){
return foo;
}
publicvoid setFoo(Boolean foo){
this.foo = foo;
}
}
classIntegerFool{
privateInteger foo;
publicInteger getFoo(){
return foo;
}
publicvoid setFoo(Integer foo){
this.foo = foo;
}
}
publicclassIntegerFoo{
/**
* @param args
*/
publicstaticvoid main(String[] args){
// TODO Auto-generated method stub
}
}
为Booleanhe Integer分别创建了两个不同的fool对象,很不方便也不效率。
改进型:
package com.liuyao.generic;
publicclassObjectFoo{
privateObject foo;
publicObject getFoo(){
return foo;
}
publicvoid setFoo(Object foo){
this.foo = foo;
}
publicstaticvoid main(String[] args){
ObjectFoo foo1 =newObjectFoo();
ObjectFoo foo2 =newObjectFoo();
foo1.setFoo(newBoolean(true));
foo2.setFoo(newInteger(3));
Boolean boolean1 =(Boolean)foo1.getFoo();
Integer i =(Integer)foo2.getFoo();
String string =(String)foo2.getFoo();
}
}
在使用时候,仍然会产生一些问题,如String在使用时候,编译时候没有错误,但是在运行时候,便会输出:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at com.liuyao.generic.ObjectFoo.main(ObjectFoo.java:25)
所以这样的改进仍然会产生问题 ,为了终极改进,引入泛型机制:
代码:
package com.liuyao.generic;
//T代表的是类型的信息,
publicclassGenericFoo<T>{
private T fooT;
public T getFooT(){
return fooT;
}
publicvoid setFooT(T fooT){
this.fooT = fooT;
}
/**
* @param args
*/
publicstaticvoid main(String[] args){
// TODO Auto-generated method stub
GenericFoo<String> sFoo =newGenericFoo<String>();
sFoo.setFooT("hello");
// 注意在,这个时候便会产生编译器错误,把运行时错误改进到了编译时期
Integer integer = sFoo.getFooT();
}
}
这里的T还可以使用A、B、C等等(一般一个大写字母即可);
这个时候编译器都会提示该传递什么类型的参数。
泛型的定义:泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。
二、java泛型语法
可以用到泛型的地方(语法定义):
1、泛型接口:
interface
Interface<T>{}
2、泛型类:
public
class
GenericFoo<T>
3:泛型方法:
public
<T> T run(T l){ return l; };
注:在泛型方法与泛型接口内部,泛型参数的类型信息被擦除了,所以在泛型信息内部不能进行其方法的调用,但是在泛型方法中,可以通过传递进来的参数,用getClass方法来获取其运行时信息。例如:
package
com.liuyao.generic;
//T代表的是类型的信息,
class Interface{
@SuppressWarnings("unchecked")
public <T> T run(T l){
Class<? extends Object> classtypeClass = l.getClass();
String nameString = classtypeClass.getName();
return (T) nameString;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Interface in = new Interface();
System.out.println(in.run(""));
System.out.println(in.run(3));
System.out.println(in.run(new Integer(4)));
}
}
output:
泛型方法的特点:
1、在方法的内部就可以知道程序在运行时的运行时信息。
2、泛型方法本身是
与类
独立的泛型,无论类是否使用泛型,方法本身的泛型不受影响。
3、static方法无法访问类的泛型参数,如果想让static方法是用泛型,在必须成为泛型方法。
4、泛型方法在使用时候不需要向泛型类那样制定泛型参数类型(类型参数判断),编译器为我们找出具体的类型。
例子:一个通用的对象生成器。
package com.liuyao.generic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
classGenetor<T>implementsIterator<T>{
privateClass<T> classType;
//hnzhongyao
publicGenetor(Class<T> classType){
super();
this.classType = classType;
}
publicboolean hasNext(){
returnfalse;
}
public T next(){
T l=null;
try{
l = classType.newInstance();
}catch(InstantiationException e){
e.printStackTrace();
}catch(IllegalAccessException e){
e.printStackTrace();
}
return l;
}
publicvoid remove(){
}
}
publicclassGenrators<T>{
publicstatic<T>Collection<T> fill(Collection<T> coll,Genetor<T> gen,int n){
for(int i =0;i<n;i++){
coll.add(gen.next());
}
return coll;
}
publicstaticvoid main(String[] args){
Collection<String> l = fill(newArrayList<String>(),newGenetor<String>(String.class),10);
System.out.println(l.size());
Iterator<String> iterator = l.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
三、泛型的擦除
从一个小例子看透泛型的擦除:
package com.liuyao.generic;
import java.util.ArrayList;
public class EraseInfo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Class<?> info1 = new ArrayList<String>().getClass();
Class<?> info2 = new ArrayList<Integer>().getClass();
System.out.println(info1.equals(info2));
}
}
输出:
结论:
ArrayList
<
String
> 与
ArrayList
<
Integer
>()具有同样的Class对象,Class对象用于描述所有该类在运行时的信息,由于该;两个类具有相同的Class对象,则这两个类的运行时信息完全相同,所以后面的制定的泛型参数的类型信息完全消失了,也就是被擦除了。(Java编程思想第四版)。
总结:泛型擦除的特点:
1、在泛型代码内部,无法获得任何有关泛型参数类型的信息。
2、无发创建对象。
3、提供了边界的泛型参数,可以提供一定的信息类型边界信息。
由于泛型的擦除特性,我们必须协助泛型,给定泛型的边界,以此告知编译器只能接受遵循这个边界的类型。
例如:
package com.liuyao.generic;
classPet{
publicvoid bark(){
System.out.println("bark");
};
}
classDogextendsPet{
publicvoid run(){
System.out.println("run");
}
}
publicclassEraseInfo2<T extendsPet>{
T obj;
publicEraseInfo2(T obj){
super();
this.obj = obj;
}
//通过指定了边界,可以调用以这个为边界所允许的方法
publicvoid doIt(){
obj.bark();
}
publicstaticvoid main(String[] args){
Dog dog =newDog();
EraseInfo2<Dog> dogl =newEraseInfo2<Dog>(dog);
dogl.doIt();
}
}
泛型擦除的代价:
1、泛型不能运用于运行时类型的操作之中。 如instanceof 和 new关键字的操作。
2、在泛型类上不提供任何的泛型参数也不会出现任何错误。
3、不同泛型参数的统一泛型对象,其Class对象完全相同,会产生混淆。
四、边界
泛型的边界可以使得参数类型附加限定条件。
作用:
1、可以安装自己的边界类型来进行方法调用。
2、可以配合通配符使用。