学习java或者软件开发的出去面试有时候项目经理经常会问到有关泛型的理解;
泛型:是指在定义类或者接口的时候可以为类和接口指定类型形参,在定义变量、定义方法时该类型形参可以当做普通的类型来使用,并且该类型形参在定义变量和创建对象时确定
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
}
}
2.1 从泛型类或者接口派生子类
当创建了带泛型声明的接口或者实现类时,可以为接口创建相应的实现类,或者从父类派生子类,但是在使用这些接口或者父类时不能再包含类型形参,比如这样写是错误的:
public class InterfaceImplement implements InterfaceGeneric<E>{}
必须这样写:
public class InterfaceImplement implements InterfaceGeneric<Integer>{}
指定一个类型,或者不指定,但是不能写<E>.
如果不指定,派生出来的类中泛型类型默认使用object类型:
public class InterfaceImplement implements InterfaceGeneric{
public object getE();
public void save(object e);
}
3、类型通配符
在定义一个方法的时候,如果该方法的参数为集合形参,集合的元素类型是不确定的,那我们该怎么定义呢?
假如如下所示的定义:
public void invoke(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");
invoke(list);//不正确,因为List<String>不是List<Object>的子类</span>
那么这样的使用方法是不正确的,因为函数的形参是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);
当直接使用List<?>这种形式时,就表明List集合可以是任何泛型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");
}
}
假如有一个画布可以画很多图形,则可以这样定义该画布类:
public class Canves {
// <span style="color:#FF0000;">这里使用List<Shape>不正确,会出现编译错误,因为List<Circle> List<Rectangle>不是List<Shape>的子类</span>
// 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、泛型方法
以上讲的都是在定义接口和类的时候声明类型形参,在该类的方法定义、变量定义和接口的方法定义、变量的定义中,该类型形参可以当作普通类型来使用。如果我们的类没有声明类型形参,但是方法却想定义类型形参怎么办呢 ,jdk1.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);
}
}
2015-12-22:写了这么多,突然想到定义泛型的类型和通配符的类型都是不确定,假如定义了一个泛型类或者泛型方法,又如何确定能在类或者方法里做什么操作呢,有待进一步深入!!!
2015-12-23:不过本人认为java泛型一般都是在定义抽象类或者接口的时候使用,用于子类或者实现类的扩展 。因为是一般的类中使用泛型没有任何意义,因为泛型的类型是未知的,不能做任何的操作!!!
2015-12-23 11:04 :昨天我提到泛型的类型不确定,所以不能做任何操作,只能使用Object类的那几个方法,这样泛型的使用就很不爽了。假如我定义了一个泛型类,里面定义了一个泛型方法,如下:
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;
}
这样岂不是很容易实现我们想要的结果,这就是类型限定!!!
2015-12-23 14:00:那么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));
}
}
如果可以就打赏一下吧,我将再接再厉,给大家分享更好的技术文章 !!!
如果可以就打赏一下吧,我将再接再厉,给大家分享更好的技术文章 !!!