泛型(Generic)学习总结

一、使用泛型的原因

1、提出问题:

public class Max {

	public static void main(String[] args){
		String str = "hello world";
		Integer number = new Integer(2);
		System.out.println(max(str, number));
	}
	public static Comparable max(Comparable o1, Comparable o2){
		if(o1.compareTo(o2) > 0){
			return o1;
		}
		else{
			return o2;
		}
	}
}

String 类和 Integer类都实现了Comparable接口,在编译时是不会报错的,但是Integer和String是无法比较的,因此在运行时会抛出  java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String。怎样才能在编译时就报错呢?

使用泛型可以指定类或方法一起工作的对象的类型,从而在编译时就找出不相容的对象(比如Integer和String不相容)。

改进代码如下:

public static <E extends Comparable<E>> E max(E o1, E o2){
		if(o1.compareTo(o2) > 0){
			return o1;
		}
		else{
			return o2;
		}
	}

二、在类,方法,接口中使用泛型

1、类

public class GenericStack<T> {
	private List<T> list = new ArrayList<T>();
	
	public void push(T t){
		list.add(t);
	}
	
	public T pop(){
		int size = list.size();
		if(size > 0){
			return list.get(list.size() - 1);
		}
		return null;
	}
}

为了定义个类为泛型类型,需要将泛型类型放在类名之后,例如:GenericStack<T>


2、方法

public class Max {

	public static void main(String[] args) {
		Integer[] numbers = { 4, 2, 6, 1, 2 };
		Integer max = Max.<Integer>max(numbers);
		System.out.println(max);
	}

	public static <E extends Comparable<E>> E max(E[] objects){
		int length = objects.length;
		if (length == 0) {
			return null;
		}

		E max = objects[0];
		for (int i = 1; i < length; i++) {
			if (max.compareTo(objects[i]) < 0) {
				max = objects[i];
			}
		}
		return max;
	}
}
为了定义一个方法为泛型类型,需要将泛型类型放在方法的返回类型之前。如
public static <E extends Comparable<E>> E max(E[] objects)


三、原始类型和通配泛型

1、问题:

定义一个泛型栈类型MyStack<T>,我们申明如下方法:

public static double max(MyStack<Number> myStack){
		...
}

但是当我们如下调用时:

MyStack<Integer> myStack = new MyStack<Integer>();
myStack.push(12);
myStack.push(11);
System.out.println("The max is " + max(myStack));

却会报错,那是因为:Number是Integer的父类,但是MyStack<Number> 不是MyStack<Integer> 的父类,因而MyStack<Number>无法引用MyStack<Integer>

改进:将方法中的变量MyStack<Number> 改为MyStack<? extends Number> 则可以通配 MyStack<Integer> 、MyStack<Double> ...各种类型。


2、通配泛型的类型有三种:?, ? extends T 或者 ? super T, 其中T是个泛型。
?称为非受限的通配,和? extends Objects一样。
? extends T 称为受限通配,表示T或者T的一个子类型,例子如上
? super T 称为下限通配,表示T或者T的一个父类型,例子如下

/*
* 将stack2中的值放到stack1中
* stack1要容纳stack2中的值,那么stack1中值得类型必须是stack2中的类型或是它的父类
*/
public static <T> void add(MyStack<? super T> stack1, MyStack<T> stack2){
	while(!stack2.isEmpty()){
		stack1.push(stack2.pop());
	}
}

3、原始类型

MyStack<Integer> myStack = new Mystack<Integer>();     这是使用泛型类同时指定具体类型为Integer

MyStack stack = new MyStack(); 这是使用泛型类但是没有指定具体类型,像这样不指定具体类型的泛型类称为原始类型,等价于 MyStack<Object> myStack = new MyStack<Object>();


四、泛型的运作原理

泛型只能存在于编译时,它存在的意义只是让编译器确认是否可以安全使用,一旦确定,编译器就会把它转为原始类型。举例如下:

1、泛型类型不受限

ArrayList<String> list = new ArrayList<String>();
list.add("hello");
当编译器确认字符串hello是符合要求的String类型,就会把这段代码变为:

ArrayList list = new ArrayList();
list.add("hello");

2、泛型类型受限

public static <E extends Number> E max(E o1, E o2)

E是Number的子类的泛型类型

编译器编译后,是

public static Number max(Number o1, Number o2)


3、总结:

a、当泛型类型不受限时,编译器编译时,会用Object类型来具体泛型类型

b、当泛型类型受限时,那么编译器会用一个受限的类型来替换它

c、不管实际的类型是什么,泛型类都是被所有的实例共享的。例如,ArrayList<Integer> list1 和 ArrayList<String> list2 的泛型类都是ArrayList,因此list1 instanceof ArrayList 和list2 instanceof ArrayList都是true;

d、泛型类型只存在于编译时,不存在运行时。

e、不能使用 new E();即 E object = new E();是错误的

f、不能使用 new E[]来申明数组

g、静态方法的参数不能有泛型,静态数据域也不能有泛型

h、异常类不能是泛型,即泛型类不能继承java.lang.Throwable


                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值