一 为什么要引入泛型这个概念?

       这里我用一个实例来简单说明。比如说:我们要设计一个表示二维坐标的类,但是因为关于坐标的表示有多种形式,比如:

       (1)整数表示:x=10    y=20

       (2)浮点型表示:x=10.5    y=20.8

       (3)字符串表示:x=””东经 50度””    y=”北纬 79度”

       因此,在我们设计的类中就不能单一的设置成int,float或String,而想要使用一个类型来接收这三种不同的数据类型,就只能使用Object。测试代码如下:

package javase.paradigm;

/**
 * 二维坐标表示
 * */
public class Point {
	private Object X;
	private Object Y;
	
	public Object getX() {
		return X;
	}

	public void setX(Object x) {
		X = x;
	}

	public Object getY() {
		return Y;
	}

	public void setY(Object y) {
		Y = y;
	}

	public static void main(String[] args) {
		Point point = new Point();
		//1 整数表示坐标
		point.setX(10);  //int --> Integer --> Object
		point.setY(20);
		
		int x = (int) point.getX();
		int y = (int) point.getY();
		System.out.println("整数表示,X坐标是:" + x + ",Y坐标是:" + y);
		
		System.out.println("******************我是华丽的分割线**********************");
		
		//2 小数表示坐标
		point.setX(10.5f);  //float --> Float --> Object
		point.setY(20.8f);
		
		float x2 = (float) point.getX();
		float y2 = (float) point.getY();
		System.out.println("小数表示,X坐标是:" + x2 + ",Y坐标是:" + y2);
		
		System.out.println("******************我是华丽的分割线**********************");
		
		//3 字符串表示坐标
		point.setX("东经 50度");  //String --> Object
		point.setY("北纬 79度");
		
		String x3 = (String) point.getX();
		String y3 = (String) point.getY();
		System.out.println("字符串表示,X坐标是:" + x3 + ",Y坐标是:" + y3);
		
	}

}

输出:

整数表示,X坐标是:10,Y坐标是:20
******************我是华丽的分割线**********************
小数表示,X坐标是:10.5,Y坐标是:20.8
******************我是华丽的分割线**********************
字符串表示,X坐标是:东经 50度,Y坐标是:北纬 79度

       通过上面设计的这个类貌似已经解决我们的需求了?但是真的是这样吗?这个类中将变量设置成Object类型,就意味着可以使用任意的Object子类来初始化,如果对变量初始化的类型和取出类型不一致,则程序在运行时会报错,出现类型转化异常。比如说这样:

                point.setX(0);
		point.setY("北纬179度");
		
		int x4 = (int) point.getX();
		int y4 = (int) point.getY();  //错误代码
		System.out.println("错误案例,X坐标是:" + x4 + ",Y坐标是:" + y4);

这段代码编译没有问题,但是运行时报错,报错信息如下:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at javase.paradigm.Point.main(Point.java:60)

错误信息已经很明显了,String类型不能转化成Integer类型。因此为了避免出现这种类型安全问题,我们就需要使用泛型

二 泛型的初步使用

(1)格式:

       类名称<具体类> 对象名称 = new 类名称<具体类>()

如:Point2<Integer> point2_1 = new Point2<Integer>();

(2)完整测试案例代码如下:

package javase.paradigm;

public class Point2<T> {
	private T var;
	
	public T getVar() {
		return var;
	}

	public void setVar(T var) {
		this.var = var;
	}

	public static void main(String[] args) {
		//1 整数
		Point2<Integer> point2_1 = new Point2<Integer>();
		point2_1.setVar(20);
		System.out.println("整数测试:" + point2_1.getVar());

		System.out.println("******************我是华丽的分割线**********************");
		
		//字符串
		Point2<String> point2_2 = new Point2<String>();
		point2_2.setVar("zifangsky的个人博客");
		System.out.println("字符串测试:" + point2_2.getVar());
	}

}

输出:

整数测试:20
******************我是华丽的分割线**********************
字符串测试:zifangsky的个人博客

将第一个例子修改成泛型:

package javase.paradigm;

public class Point3<T> {
	private T x;
	private T y;
	
	public T getX() {
		return x;
	}

	public void setX(T x) {
		this.x = x;
	}

	public T getY() {
		return y;
	}

	public void setY(T y) {
		this.y = y;
	}

	public static void main(String[] args) {
		//1 整数表示
		Point3<Integer> point3_1 = new Point3<Integer>();
		point3_1.setX(10);
		point3_1.setY(20);
		
		int x1 = point3_1.getX();
		int y1 = point3_1.getY();
		System.out.println("整数表示,X坐标是:" + x1 + ",Y坐标是:" + y1);
		
		System.out.println("******************我是华丽的分割线**********************");
		
		//2 字符串表示
		Point3<String> point3_2 = new Point3<String>();
		point3_2.setX("东经 50度");
		point3_2.setY("北纬 79度");
		
		String x2 = point3_2.getX();
		String y2 = point3_2.getY();
		System.out.println("字符串表示,X坐标是:" + x2 + ",Y坐标是:" + y2);
		
	}

}<span style="font-family:'sans serif', tahoma, verdana, helvetica;font-size:16px;line-height:1.5;"></span>

三 一个类中定义多个泛型类型

package javase.paradigm;

public class Nodepad<K, V> {
	private K key;
	private V value;
	
	public void setKey(K key) {
		this.key = key;
	}

	public void setValue(V value) {
		this.value = value;
	}
	
	public void print(){
		System.out.println("键:" + key + ",值:" + value);
	}

	public static void main(String[] args) {
		Nodepad<String, Integer> nodepad = new Nodepad<String, Integer>();
		nodepad.setKey("zifangsky");
		nodepad.setValue(100);
		//测试
		nodepad.print();
	}

}

输出:

键:zifangsky,值:100

四 泛型方法的使用

(1)格式:

       [访问权限]<泛型标志> 泛型标志 方法名称([泛型标志 参数名称])

(2)测试代码:

package javase.paradigm;

public class MethodDemo {
	public <T> T getData(T t){
		return t;
	}
	
	public void print(){
		System.out.println("zifangsky");	
	}
	
	public static void main(String[] args) {
		MethodDemo methodDemo = new MethodDemo();
		methodDemo.print();
		
		int i = methodDemo.getData(10);
		System.out.println("int: " + i);
		
		String str = methodDemo.getData("hello world");
		System.out.println("String: " + str);
	}

}

输出:

zifangsky
int: 10
String: hello world

五 泛型接口的定义和两种实现方式

(1)泛型接口的定义:

package javase.paradigm;

public interface Info<T> {
	public T getVar();
}

(2)接口的实现方式一:

在子类的定义上申明泛型类型:

package javase.paradigm;

public class InfoImpl_1<T> implements Info<T> {
	private T var;
	
	public InfoImpl_1(T var) {
		this.var = var;
	}

	public T getVar() {
		return this.var;
	}

	public void setVar(T var) {
		this.var = var;
	}

}

(3)接口的实现方式二:

直接在接口中指定具体类型:

package javase.paradigm;

public class InfoImpl_2 implements Info<String> {
	private String var;
	
	public InfoImpl_2(String var) {
		this.var = var;
	}

	public String getVar() {
		return this.var;
	}

	public void setVar(String var) {
		this.var = var;
	}
	
}

六 一个综合实例

(1)简单分析:

       这里设计了Person这个类,但是一个人可能有多种信息展示形式,比如说:个人基本信息(姓名,性别,年龄。。。),联系方式(电话,地址,邮编。。。)。因此在Person中的信息类型就可以考虑申明为泛型。接着设计了一个空接口:Message和它的两个子类:Contact和Introduction,分别表示:联系方式和基本信息。

       在对Person进行定义的时候用了:class Person<T extends Message> ,这里的意思是这个泛型T只能是Message这个接口的子类,也就是说只能是我们先前定义的Contact和Introduction,避免了传递进来我们所不需要的其他信息

(2)实例代码:

package javase.paradigm;
/**
 * 定义标识接口
 * */
interface Message{
	
}

/**
 * 第一个子类,联系方式
 * */
class Contact implements Message{
	private String address;
	private String telphone;
	private String zipcode;
	public Contact(String address, String telphone, String zipcode) {
		this.address = address;
		this.telphone = telphone;
		this.zipcode = zipcode;
	}
	/**
	 * 重写toString方法
	 * */
	public String toString(){
		return "联系方式:\n" + 
				"\t|- 电话: " + telphone + "\n" + 
				"\t|- 地址: " + address + "\n" +
				"\t|- 邮编: " + zipcode + "\n";
	}
}

/**
 * 第二个子类,个人信息
 * */
class Introduction implements Message{
	private String name;
	private String sex;
	private int age;
	private String job;
	public Introduction(String name, String sex, int age, String job) {
		this.name = name;
		this.sex = sex;
		this.age = age;
		this.job = job;
	}
	/**
	 * 重写toString方法
	 * */
	public String toString(){
		return "基本信息:\n" + 
				"\t|- 姓名: " + name + "\n" + 
				"\t|- 性别: " + sex + "\n" +
				"\t|- 年龄: " + age + "\n" +
				"\t|- 工作: " + job + "\n";
	}
}

/**
 * 定义泛型,并且T必须是Message这个接口的子类
 * 避免了传递进来其他不需要的类型
 * */
public class Person<T extends Message> {
	private T message;
	
	public Person(T message) {
		this.message = message;
	}
	
	public String toString(){
		return message.toString();
	}

	public static void main(String[] args) {
		//1 将泛型实例化成Contact类型
		Person<Contact> person_1 = new Person<Contact>(new Contact("http://www.zifangsky.cn", "10086", "1024"));
		System.out.println(person_1);
		
		System.out.println("******************我是华丽的分割线**********************");
		
		//2 将泛型实例化成Introduction类型
		Person<Introduction> person_2 = new Person<Introduction>(new Introduction("zifangsky", "男", 256, "程序猿"));
		System.out.println(person_2);
	}

}

输出:

联系方式:
	|- 电话: 10086
	|- 地址: http://www.zifangsky.cn
	|- 邮编: 1024

******************我是华丽的分割线**********************
基本信息:
	|- 姓名: zifangsky
	|- 性别: 男
	|- 年龄: 256
	|- 工作: 程序猿