JAVA基础之泛型学习总结

    所谓泛型就是“参数化类型”,是JDK1.5后引入的。在java的集合当中,常用的List,Map都用到了泛型,可以指定集合中放置的元素类型,不指定默认为Object类型。使用泛型的优点主要有类型安全和取消强制类型转换。下面从一小段代码来看一下泛型的使用:

public class GenericMain {

	public static void main(String[] args) {
		List list1 = new ArrayList();
		list1.add("apple");
		list1.add(123);
		for(Object obj : list1) {
			String value = (String) obj;	//1:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
			System.out.println(value);
		}
		
		List<String> list2 = new ArrayList<String>();
		list2.add("hello");
		//list2.add(123);	//2
		list2.add("world");
		for(Object obj : list2) {
			String value = obj;
			System.out.println(value);
		}
		
	}
}

    上面的代码中,list1实例在初始化时,未指定元素类型,默认为Object,所以在加入元素的时候,既可以加入字符串类型,也可以加入整型。同样的,取出元素的时候也是Object类型,一般操作会进行类型转化,容易出现1中的ClassCastException异常。所以一般使用集合时,会指定集合中元素的类型,类如list2,初始化时指定了集合类型为String,这样在加入非String类型元素的时候会直接编译报错,例如2中编译报错。除此之外,在取出的时候默认取出的类型为String,这样就不会有类型转化错误,所以使用泛型也有一定的潜在性能优化。

    可以看一下ArrayList的部分源码:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    transient Object[] elementData; // non-private to simplify nested class access

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    //其他先省略
}

    从上面AbstractList的源码可以看出来,List,ArrayList都使用了泛型,定义了参数化类型<E>,在使用时指定具体类型,例如String后。在编译的时候,这个List里面的E都会认为是String,在get和set的时候也是这样,所以不能放入非String类型的数据。

 

    下面进入正文,泛型主要用于泛型接口,泛型类,泛型方法。

一:泛型接口

   定义泛型接口,和普通泛型接口相比,在接口名后面用<泛型类型变量>,当需要多个参数时,用逗号隔开。上面的List<E>就是一个典型的泛型接口。

public interface DataHandlerI<E, T> {
	
	void setData(E data);
	
	E getData();
}

    这里的E是随便定义的,常用的有

  • E  elemet 元素,一般集合会用E;
  • K  key 键;
  • V  value 指,K,V常用与键值对的集合,例如Map;
  • T  一般用于各种类型,多个泛型类型的时候,一般后面加数字,例如T1, T2;

     接口DataHandlerI定义了参数化类型E,T,但是实现这个接口的类不一定是泛型类。例如下面的AbstractDataHandler和StringDataHandler。

public class AbstractDataHandler<E> implements DataHandlerI<E> {

	private E data;
	
	public void setData(E data) {
		this.data = data;
	}

	public E getData() {
		return this.data;
	}

}
public class StringDataHandler implements DataHandlerI<String>{

	String data;
	
	public void setData(String data) {
		this.data = data;
	}

	public String getData() {
		return this.data;
	}

}

 

二:泛型类

泛型类和泛型接口差不多,就是类在定义时声明参数化类型,可以声明多个,逗号隔开。

public class ResultData <E>{
	public List<E> result;
	//public static List<E> result1;	//1:Cannot make a static reference to the non-static type E
	
	ResultData(){
		result = new ArrayList<E>();
	}
	
	public void addResult(E result) {
		this.result.add(result);
	}
	
	public E getResult() {
		if(result.size() > 0) {
			return this.result.remove(0);
		}else
			return null;
	}
	
	public E getResult(int index) {
		return this.result.get(index);
	}
	
//	public static E getResult1(int index) {  //2
//		return this.result.get(index);
//	}
	
	//泛型函数
	public static <T> T[] toArray(T[] param) {
		return param;
	}
	
	public static void main(String[] args) {
		ResultData<String> result1 = new ResultData<String>();
		result1.addResult("abc");
		ResultData<Integer> result2 = new ResultData<Integer>();
		result2.addResult(123);
		ResultData<Double> result3 = new ResultData<Double>();
		result3.addResult(13.14);
		
		String str = result1.getResult();
		System.out.println(str);
		
		Class class2 = result2.getClass();
		Class class3 = result3.getClass();
		if(class2 == class3) {
			System.out.println("泛型类指定不同的具体类型后,是同一个类");
		}
	}
}
  • 泛型类在类上定义的泛型类型(E)不能涉及到静态变量和静态方法,例如1,2编译时会报错Cannot make a static reference to the non-static type E;
  • 泛型类里可以使用其他泛型变量的泛型方法,例如toArray,这个方法里面没有设计到类定义到参数类型E,所以可以时static类型的;
  • 在指定具体的参数类型后,只是在编译阶段检测类型,在运行的时候,其实时同一个类,类是一样的;

 

三:泛型方法

泛型方法定义有两种:

  • 泛型类定义的泛型类型,在类里面使用该泛型类型的方法;
  • 在方法关键字后面,返回类型前面,用<泛型类型变量>的方式先声明泛型类型变量名,方法的参数,返回类型,方法体中可以使用这个泛型类型变量。
public class GenericUtil {
	//比较大小,返回三个指中最大的那个
	public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
		T max = x;
		if(y.compareTo(max) > 0) {	// y>max
			max = y;
		}
		if(z.compareTo(max) > 0) {	//z>max
			max = z;
		}
		return max;
	}
	
	public static void main(String[] args) {
		int max = GenericUtil.maximum(3, 1, 2);
		System.out.println("the max value =" + max);
		
		String max1 = GenericUtil.maximum("china", "apple", "bear");
		System.out.println("the max value =" + max1);
	}
}

 

四:泛型通配符

所有的类型对象父类都是Object,在泛型中我们可以使用<?>来通配一类类型。

public class GenericUtil {
	public static void print(List<?> list) {
		System.out.println("打印内容为:"+list.get(0));
	}

	public static void main(String[] args) {
		List<String> list1 = new ArrayList<String>();
		list1.add("hello");
		
		List<Integer> list2 = new ArrayList<Integer>();
		list2.add(110);
		
		GenericUtil.print(list1);
		GenericUtil.print(list2);
		
	}
}

上面的print方法,打印列表的第一个元素,这里List<?>逻辑上可以看做是List<String>,List<Integer>等的父类。可以用通配符实现一些和具体类型无关的方法。

 

五:泛型上限和下限

泛型上限 extends

    在上面举例泛型方法maximum中,定义泛型类型变量的时候使用了<T extends Comparable<T>>,这里的意思是类型T一定是Comparable类型的子类。还例如:

public class GenericUtil {
	
	public static <T extends Number> void print(T x){
		System.out.println("打印内容为:"+x);
	}

	public static void main(String[] args) {
		//GenericUtil.print("String");	//1:Bound mismatch: The generic method print(T) of type GenericUtil is not applicable for the arguments (String). 
		GenericUtil.print(123);
		GenericUtil.print(43.23);
	}
}

上面的例子中,print方法的参数继承子Number,所以可以成功打印数值型参数,非数值型的变量例如字符串在编译阶段就会报错Bound dismatch。可以通过泛型上限限制参数类型范围。

泛型下限 super

和泛型上限相反,泛型上限是指定参数类型最低为。例如<T super Number>,print方法的参数类型只能为Number,Object

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值