1.泛型的本质:
泛型就是广泛的类型,就是在不同类型之间都可以使用的就叫做泛型,使用的是类型里面的方法,所以如果要使用泛型就需要保证使用泛型的类需要有共同的方法,泛型(generics,genericity)又称为“参数类型化(parameterized type)”或“模板(templates)”,是和继承(inheritance)不同而互补的一种组件复用机制。
继承和泛型的不同之处在于——在一个系统中,继承层次是垂直方向,从抽象到具体,而泛型是水平方向上的。当运用继承,不同的类型将拥有相同的接口,并获得了多态性;当运用泛型,将拥有许多不同的类型,并得以相同的算法作用在它们身上。因此,一般说来,当类型与实现方法无关时,使用泛型;否则,用继承,既然如此可以更进一步的总结为,使用泛型的类之间它们应该有共同的方法,否则泛型也就没有意义了。
而且本身泛型的T这个字符因为代表的是Object类型,所以任何的关于Object类型可以使用的方法和参数它都可以使用。
2.泛型分类:
泛型类的本质是:让泛型类里面的类类型的参数可识别化(在真正使用的时候需要指定参数类型)。
泛型方法的本质是:让泛型方法里面的类类型参数可识别化(在真正使用的时候需要指定参数类型)。
泛型接口的本质是:让泛型接口里面的类类型参数可识别化(在真正使用的时候需要指定参数类型)
学习java或者软件开发的出去编程有时候项目经理经常会问到有关泛型的理解;
泛型:是指在定义类或者接口的时候可以为类和接口指定类型形参,在定义变量、定义方法是该类型形参可以当做普通的类型来使用,并且该类型形参在定义变量和创建对象的确定
注:泛型的好处
1.类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。
2.消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
3.潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。
例子:
public void write(Integer i, Integer[] ia);
public void write(Double d, Double[] da);
泛型版本
public <T> void write( T t, T[] ta);
注2:泛型的规则
泛型在使用中还有一些规则和限制:
1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(Java.lang.String);
3.具体使用
1、定义泛型类,程序实例代码如下:
public class GenericClass<E> {
/**
* @param args
* 自定义泛型类
*/
private E e;//变量
public GenericClass(){
}
public GenericClass(E e){
this.e=e;
}
public E getE(){//返回值类型
return e;
}
public void println(E e){//函数参数
System.out.println(e);
}
public static void main(String[] args) {
GenericClass<Integer> gc=new GenericClass<Integer>();
gc.println(1111111);
GenericClass<String> gc1=new GenericClass<String>();
gc1.println("string");
}
}
2、实现泛型接口,在实现泛型接口或者类的时候,可以不指定类型形参,但是编译的时候会出现警告
public interface InterfaceGeneric<E> {
public E getE();
public void save(E e);
}
public class InterfaceImplement implements InterfaceGeneric<Integer>{
/**
* @param args
*/
@Override
public Integer getE() {
// TODO Auto-generated method stub
return null;
}
@Override
public void save(Integer e) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
copy
3、类型通配符
在定义一个方法的时候,如果该方法的参数为集合形参,集合的元素类型是不确定的,那我们该怎么定义呢?
假如如下所示的定义:
// public void println(List<Object> list){//函数参数
// int count=list.size();
// for(int i=0;i<count;i++){
// System.out.println(list.get(i));
// }
// }
List<String> list=new ArrayList<String>();
list.add("a");
//println(list);不正确,因为List<String>不是List<Object>的子类
那么这样的使用方法是不正确的,因为函数的形参是List<Object>类型的,而实参是List<String>类型的,但是需要注意的一点是List<String>并不是List<Object>的子类,所以这样写是不正确的,那么在这个list里的元素类型不确定的情况下怎么定义这个函数呢,这就用到了类型通配符;在java中类型通配符是一个?;类型通配符是所有泛型List的父类,在定义函数的时候用?代替类型,就可以在以后使用的时候定义各种各样的类型的List集合,如下的程序实例所示:
public void println(List<?> list){//函数参数List使用类型通配符
int count=list.size();
for(int i=0;i<count;i++){
System.out.println(list.get(i));
}
}
List<String> list=new ArrayList<String>();
list.add("a");
gc.println(list);
4、类型通配符上限
定义一个形状类,有一个画图的方法,可以画各种各样的形状,则可以定义为接口如下所示:
public interface Shape {
public void draw();
}
定义一个圆类和矩形类,分别实现上面定义的接口,然后重写方法draw();
public class Rectangle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("draw a rectangle");
}
}
public class Circle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("draw a circle");
}
}
假如有一个画布可以画很多图形,则可以这样定义该画布类:copy
public class Canves {
/**
* @param args
*/
//这里使用List<Shape>不正确,会出现编译错误,因为List<Circle> List<Rectangle>不是List<Shape>的子类
// public void draw(List<Shape> shapeList){
//
// }
/*
*可以考虑使用类型通配符的方式List<?>
*但是这里使用的类型通配符是配的所有的类型,所以加进去的元素就是Object类型的
*这里需要强制类型转换,降低了程序的执行效率;所以可以使用java提供的类型通配符的上限定义方法
**/
// public void draw(List<?> shapeList){
// for(Object obj:shapeList){
// Shape shape=(Shape)obj;
// shape.draw();
// }
// }
/*
*这里使用类型通配符的上限的形式定义list集合的元素类型List<? extends Shape>
* */
public void draw(List<? extends Shape> shapeList){
for(Shape shape:shapeList){
shape.draw();
}
}
public static void main(String[] args) {
Canves c=new Canves();
List<Circle> listCircle=new ArrayList<Circle>();
listCircle.add(new Circle());
List<Rectangle> listRectangle=new ArrayList<Rectangle>();
listRectangle.add(new Rectangle());
c.draw(listCircle);// 1
c.draw(listRectangle);// 2
}
}
draw()方法的参数里list集合的类型使用的是类型通配符的上限形式,这样既可以往list集合中添加不同的Shape类子类的对象,而且不用进行强制类型转换。
5、泛型方法
泛型方法的使用效果和类型通配符非常相似
public class GenericMethod {
//泛型方法
public <E> void genericmethod(List<E> a){
for(E t:a)
System.out.println(t.toString());
}
public static void main(String[] param){
List<String> list=new ArrayList<String>();
list.add("dfd");
list.add("dfd");
list.add("dfd");
list.add("dfd");
new GenericMethod().genericmethod(list);
List<Integer> list1=new ArrayList<Integer>();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
new GenericMethod().genericmethod(list1);
}
} <span style="font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; line-height: 18px; background-color: inherit;"> </span>
public static <T> T get(T t1,T t2) {
if(t1.compareTo(t2)>=0);//编译错误
return t1;
}
很明显会出现编译错误,因为泛型的类型不确定,根本找不到我们调用的方法。可是如果我定义的泛型类里的泛型方法就是要把比较写好呢,这样子类不是就不用重写了吗,怎么办呢,用类型限定吧。就是说T没有我们想要的方法,那我们就让T继承有我们想要的方法的接口或者类。比如:
public static <T extends Comparable> T get(T t1,T t2) { //添加类型限定
if(t1.compareTo(t2)>=0);
return t1;
}
6.java泛型中的T和?有什么区别
那么Java泛型中的T和?有什么区别呢,正如下面两个方法是一样的
public void printlns(List<?> list){//函数参数List使用类型通配符,泛型方法
int count=list.size();
for(int i=0;i<count;i++){
System.out.println(list.get(i));
}
}
public <T> void println(List<T> list){//函数参数List使用类型通配符,泛型方法
int count=list.size();
for(int i=0;i<count;i++){
System.out.println(list.get(i));
}
}
上面有泛型的定义和赋值;当在赋值的时候,上面一节说赋值的都是为具体类型,当赋值的类型不确定的时候,我们用通配符(?)代替了
参考:http://blog.csdn.net/sun_abc/article/details/5906765