温故而知新——泛型

143 阅读5分钟

本篇文章我们来介绍下泛型相关的知识。

1. 使用泛型的好处

泛型是JDK1.5出现的安全机制

使用泛型的好处:

  • 将运行时期的问题ClassCastException转移到了编译时期。
  • 避免了强制类型转换的麻烦。

泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。

2. 泛型擦除和泛型补偿

代码运行时,会将.java文件中的泛型去掉,生成的classA文件是不带泛型的。

为什么擦除泛型呢?

因为为了兼容运行时的类加载器。

取出元素的时候不加强转了,那你还知道取出的类型是啥类型吗?泛型补偿

在以前的类加载器上写一个反省补偿程序。

在运行时,通过元素的类型进行转换动作,使用者不用专门在做强制类型转换了。

	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<>();
		list.add("abc");
		list.add("haha");
		Iterator<String> it = list.iterator();
		while (it.hasNext()) {
			String string = (String) it.next();  // 有强转
			System.out.println(string);
		}
	}

运行结果:

将上面的代码中while循环体中(String) it.next()的强制类型转换去掉,代码也能正常运行,就是因为有泛型补偿机制。

3. 泛型类/泛型接口/泛型方法

泛型的其他作用:

提供设计上的便捷,提高代码复用性。

  • 自定义泛型类:在类名称后面定义泛型ABC,在类内容中使用泛型ABC
  • 自定义泛型方法:在方法的返回值类型前面定义泛型W,在方法参数列表中使用泛型W
  • 自定义泛型接口:将泛型定义在接口上。

3.1 泛型类

泛型类:在类名后加泛型的类叫泛型类。

泛型类什么时候用?

当类中操作的引用数据类型不确定的时候,使用泛型来表示。在jdk1.5后使用泛型来接收类中要操作的引用数据类型。

我们想写一个工具,工具在setXxx和getXxx方法中需要传入的类型不确定,那么我们可以将参数类型设为Object,这样就既可以传入Student类型,也可以传入Person类型。

但是,在运行时候发现,很容易出现如下类型转换异常。如下图:

package com.yansong.p1.generic;

import com.yansong.bean.Person;
import com.yansong.bean.Student;

public class GenericDemo01 {
	public static void main(String[] args) {
		Tool tool = new Tool();
		tool.setObj(new Person());
		Student stu = (Student)tool.getObj();
	}
}

package com.yansong.p1.generic;

public class Tool {
	private Object obj;

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}
	
}

引入泛型类

在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。

泛型类什么时候用呢?

当类中的操作的引用数据类型不确定的时候,就使用泛型来表示

泛型类是在类名称后面加泛型的类。比如下面代码中的ABC,由于引用数据类型不确定,我们就可以使用泛型来解决,在使用的时候传入Person对象/Student对象/Worker对象都看可以。

public class ToolWithGeneric<ABC>  //在类类名称后面定义泛型

用法:

package com.yansong.p1.generic;
public class ToolWithGeneric<ABC> {   //在类名称后面定义泛型ABC
	private ABC object; // 在类内容中使用泛型ABC

	public ABC getObject() {
		return object;
	}

	public void setObject(ABC object) {
		this.object = object;
	}	
}

使用自定义泛型类:

  • 重复利用。ToolWithGeneric这个类使用了泛型,没有指定具体操作的数据类型。在使用的时候将ABC才根据需要确定成Person类或Student类。
  • 提高安全性。下图中的代码,当18行代码中将泛型指定成Student类型的时候,在19行给steXXX的时候传入Person就会报错。
  • 在13行代码中指定泛型为Student,15行获取对象的时候也不用专门在加强制类型转换了。

3.2 泛型方法

将泛型定义在方法上。

定义位置在方法返回值的前边,修饰字符的后边。

另外:

  • 当方法静态时,不能访问类上定义的泛型,只能将泛型定义在方法上。
  • 一旦使用了泛型,就无法使用某个类特有的方法了,只能用Object的方法,为什那么呢?因为泛型真正在使用的时候时候是哪个类不确定啊,所以就不能使用特有的方法,而Object类的方法是所有类都有的方法,所以可以使用。
    // 将泛型定义在方法上
	public <W> void show(W str){
		System.out.println("show : "+str.toString());
	}
	
	public static <Y> void method(Y obj){
		System.out.println("method:"+obj);
	}

具体演示: 我现在想创建一个show方法,它可以对任何对象进行显示,应该怎么做呢? 分析一下,其实就是方法参数类型不确定。

tips:当然了,不使用泛型的话我们可以将参数类型定为Object。但是这们讲的是泛型。

package com.yansong.p1.generic;

public class ToolWithGeneric<ABC> {
	private ABC object;

	public ABC getObject() {
		return object;
	}

	public void setObject(ABC object) {
		this.object = object;
	}
	
	//需要show的类型不确定,
	//我们自定义个一个泛型W,并在show方法上使用这个泛型,
	//具体使用的时候W可以是 double,String,Person,Object
	public <W> void show(W w) {
		System.out.println("show : "+w.toString());
	}
}

package com.yansong.p1.generic;

import hashdemo.Person;

public class GenericDemo2 {
	public static void main(String[] args) {
		
		ToolWithGeneric<String> toolWithGeneric = new ToolWithGeneric<>();
		
		toolWithGeneric.show(new Integer(100));
		toolWithGeneric.show("abc");
		toolWithGeneric.show(3.14159);
		toolWithGeneric.show(new Person("yansong", "男"));
	}
}

运行结果:

3.3 泛型接口

将泛型定义在接口上。

package com.yansong.p1.generic;

import com.yansong.bean.Student;

public class GenericDemo3 {
	public static void main(String[] args) {
		StrInterfaceImpl strImpl = new StrInterfaceImpl();
		strImpl.show("abc");
		
		StudentInterfaceImpl stuImpl = new StudentInterfaceImpl();
		stuImpl.show(new Student("霍建华", "男", 115, "研究生2年级"));
		
	}
}


//泛型接口,将泛型定义在接口上。 
interface Interface<T>{
	public void show(T t);
}

//实现类在具体使用的时候将泛型明确,比如这里使用的String
class StrInterfaceImpl implements Interface<String>{
	public void show(String str){
		System.out.println("show :"+str);
	}
}

//实现类在具体使用的时候将泛型明确,比如这里使用的String
class StudentInterfaceImpl implements Interface<Student>{
	public void show(Student stu){
		System.out.println("show :"+stu.toString());
	}
}

运行结果: