一 为什么要引入泛型这个概念?
这里我用一个实例来简单说明。比如说:我们要设计一个表示二维坐标的类,但是因为关于坐标的表示有多种形式,比如:
(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 |- 工作: 程序猿