Java学习笔记————泛型程序设计
1. 泛型程序设计
1.1 泛型的引出
要求设计一个可以表示坐标的类(X,Y)但是此坐标要可以同时满足以下三种集中要求:
·X=10、Y=100 ·X=10.3、Y=50.2 ·X=”东经180°”、Y=”北纬210°”
分析:
因为现在的程序中需要接受三种数据类型的数据,所以呢为了保证程序的正确性,最好使用Object类完成,因为Object可以接受任意的引用数据类型:
·数字 à 自动装箱操作 à Object ·字符串 à Object
按照以上的特点,完成程序:
class Point { private Object x; private Object y; public Object getX() { return x; } public void setX(Object x) { this.x = x; } public Object getY() { return y; } public void setY(Object y) { this.y = y; } } |
此时Point类完成了,那么设置一个整形数字,来观察是否可以操作。
public class GenDemo01 { public static void main(String[] args) { Point p = new Point(); p.setX(10); // 设置坐标。int --> Integer --> Object p.setY(10);// 设置坐标。int --> Integer --> Object int x = (Integer)p.getX() ; // 取出X:Object --> Integer --> int int y = (Integer)p.getY() ; // 取出X:Object --> Float --> float System.out.println("x = " + x); System.out.println("y = " + y); } }
|
下面继续完成设置小数的操作:
public class GenDemo02 { public static void main(String[] args) { Point p = new Point(); p.setX(10.3f); // 设置坐标。float --> Float --> Object p.setY(10.6f);// 设置坐标。float --> Float --> Object float x = (Float)p.getX() ; // 取出X:Object --> Float --> float float y = (Float)p.getY() ; // 取出X:Object --> Float --> float System.out.println("x = " + x); System.out.println("y = " + y); } } |
下面继续设置字符串最为X和Y的坐标:
public class GenDemo03 { public static void main(String[] args) { Point p = new Point(); p.setX("东经120度"); // 设置坐标。String --> Object p.setY("北纬230度");// 设置坐标。String --> Object String x = (String)p.getX() ; // 取出X:Object --> String String y = (String)p.getY() ; // 取出X:Object --> String System.out.println("x = " + x); System.out.println("y = " + y); } } |
此时基本点的功能已经完成了,但是此种操作是否存在问题呢???
在此操作之中,可以发现所有的内容都是Object进行操作的,那么就意味着,可以设置任意的类型,即:X可以使整型,Y可以使字符串类型。
public class GenDemo04 { public static void main(String[] args) { Point p = new Point(); p.setX(10); // 设置坐标。int --> Integer --> Object p.setY("北纬230度");// 设置坐标。int --> Integer --> Object int x = (Integer)p.getX() ; // 取出X:Object --> Integer --> int int y = (Integer)p.getY() ; // 取出X:Object --> Float --> float System.out.println("x = " + x); System.out.println("y = " + y); } } |
此时程序编译的时候不存在任何问题,但是在执行的时候却出现了以下异常。
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at com.java.genericsdemo01.GenDemo04.main(GenDemo04.java:20) |
之所以会这样,是因为在程序中所有的属性都可以向Object进行转换,那么,此程序的入口就显得不那么规范了,而且存在安全漏洞。
但是从之前所学过的全部代码来看,此处孩子能应用到这里了。没有更好的方法了
1.2 泛型<1>基础知识
JDK 1.5 之后出现了新的技术 —— 泛型,此技术的最大特点就是类中的属性的类型可以由外部决定,而且声明的时候应该采取如下形式:
class 类名称 <泛型类型,泛型类型,……>{ } |
那么现在采用如上的格式来修之前的操作。
package com.java.genericsdemo02; public class Point<T> { // 表示坐标类 private T x; // X 的属性类型有外部决定 private T y; // 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; } } |
此时,程序中加入了泛型之后,可以发现一切操作类型都不在由程序固定设置。而是在实例化对象的时候在外部进行了指定。
package com.java.genericsdemo02; public class GenDemo05 { public static void main(String[] args) { Point<Integer> p = new Point<Integer>(); p.setX(10); // 设置坐标。int --> Integer --> Object p.setY(20);// 设置坐标。int --> Integer --> Object System.out.println("x = " + p.getX()); System.out.println("y = " + p.getY()); } } |
发现此时在使用Point类的时候,需要加入一个属性类型的声明。而且加入之后再取出属性值的时候本身也变得非常容易,不需要再使用向下转型了。
而且,使用上面的操作有一点点最方便之处,如果此时设置的内容不是整型,那么程序中将出现错误。
package com.java.genericsdemo02; public class GenDemo06 { public static void main(String[] args) { Point<Integer> p = new Point<Integer>(); p.setX(10); // 设置坐标。int --> Integer --> Object p.setY[1]("北纬230度");// 设置坐标。int --> Integer --> Object System.out.println("x = " + p.getX()); System.out.println("y = " + p.getY()); } }
|
加入泛型之后可以对程序的操作起到更加安全的目的.
1.3 泛型<2>注意点
在使用泛型操作的时候,实际上有很多小的注意点,例如:构造方法上依然可以使用泛型或者有一种可以成为泛型的擦除。
1.3.1 在构造方法上引用泛型
一般在开发中,经常使用造方法设置属性的内容,那么此时实际上依然可以使用泛型的类型。
package com.java.demo03; public class Point<T> { private T x; private T y; public Point(T x, T y) { this.setX(x); this.setY(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; } } |
那么此时在调用的时候就需要使用构造方法设置内容,当然,设置的内容依然由泛型本身决定。
package com.java.demo03; public class GenDemo07 { public static void main(String[] args) { Point<Integer> p = new Point<Integer>(10, 20); int x = p.getX(); int y = p.getY(); System.out.println("x = " + x); System.out.println("y = " + y); } } |
1.3.2 擦除泛型
如果在使用的时候没有指定泛型类型的话,则表示擦除泛型类型。
泛型一旦擦除之后,就按照Object进行接收,以保证程序不发生任何错误。
package com.java.demo03; public class GenDemo08 { public static void main(String[] args) { Point p = new Point(10, 20); int x = (Integer) p.getX(); int y = (Integer) p.getY(); System.out.println("x = " + x); System.out.println("y = " + y); } } |
但是在以上的操作代码中,依然会存在警告信息。那么应该如何去掉警告信息呢???
package com.java.demo03; public class GenDemo08 { public static void main(String[] args) { Point<Object> p = new Point<Object>(10, 20); int x = (Integer) p.getX(); int y = (Integer) p.getY(); System.out.println("x = " + x); System.out.println("y = " + y); } } |
但是,以上的操作虽然去掉了警告信息,但是有些多余,建议在开发者一般不要擦除泛型。
1.4 通配符
在泛型的操作中通配符的使用较多,而且在日后的系统类库中有很多地方都要使用这些操作
例如,现在又如下代码:
package com.java.demo04; public class Test { public static void main(String[] args) { Object obj ="Hello World!!!"; } } |
以上的语法实际上是进行了向上的转型,因为String 是 Object的子类,但是在泛型中却没有此概念。
1.4.1 ?
在进行对象转型的时候可以使用自动的向上转型,但是在使用泛型的时候却没有此种操作。
package com.java.demo04; public class Point<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; } } |
那么下面定义两个Point类的对象。
package com.java.demo04; public class GenDemo09 { public static void main(String[] args) { Point<Object> p1 = new Point<Object>(); Point<Integer> p2 = new Point<Integer>(); p1 = p2; // 此时根本无法进行转换。 } } |
此时的程序根本就无法进行转换的操作,此时程序实际上就已经不完全属于对象转换操作了,而属于一个大的类型和小的类型划分。
例如:将“Point<Object> p1 = new Point<Object>();”表示为商场的全部商品,而“Point<Integer> p2 = new Point<Integer>();”表示每一个顾客购买的商品,如果现在执行“p1 = p2;”那么就意味着,此顾客所购买的商品就是商场的全部商品,这样肯定说不通,所以说不能接收。
不能使用以上的方式接收最大的影响在于方法的参数接收。
package com.java.demo04; public class GenDemo10 { public static void main(String[] args) { Point<Object> p1 = new Point<Object>(); Point<Integer> p2 = new Point<Integer>(); fun(p1); fun(p2); } public static void fun(Point point){ // 表示此时可以接受任意的类型。 System.out.println(point.getX()); System.out.println(point.getY()); } } |
此时程序可以正常编译运行,但是“fun(Point point)”处会出现警告信息,现在不想让其出现警告信息,那么可以进行以下操作:
package com.java.demo04; public class GenDemo10 { public static void main(String[] args) { Point<Object> p1 = new Point<Object>(); Point<Integer> p2 = new Point<Integer>(); fun(p1); fun(p2); } public static void fun(Point<?> point){ // 表示此时可以接受任意的类型。 System.out.println(point.getX()); System.out.println(point.getY()); } } |
程序中的“?”表示可以接收任意类型的泛型类型,但是只能是接收和输出,并不能修改。
1.4.2 泛型的上限
上限就是指一个操作泛型的最大的操作父类,例如,现在最大的上限设置成“Number”那么此时,所能够就收的类型只能是Number及其子类(Integer)。
泛型上限通过以下语法完成:
? extends 类 |
例如:在Point类中只能设置数字类型的坐标。
package com.java.demo05; public class Point<T extends Number> { // 最高只能是Number类型。 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; } } |
以上的泛型类型明确的指出,最大的操作父类是Number,设置的内容只能是Integer、Float等等。
package com.java.demo05; public class GemDemo11 { public static void main(String[] args) { Point<Integer> p1 = new Point<Integer>(); // 设置的是Number的子类。 } } |
如果此时设置的泛型类型是字符串的话,则也会出现错误。
package com.java.demo05; public class GemDemo12 { public static void main(String[] args) { Point<String> p1 = new Point<String>(); // 设置的是Number的子类。 } } |
|
|
而且泛型上限也可以在方法上使用,例如,接收参数:
package com.java.demo05; public class GenDemo13 { public static void main(String[] args) { Point<Integer> p2 = new Point<Integer>(); fun(p2); } public static void fun(Point<? extends Number> point){ //此时可以接受Number的子类。 System.out.println(point.getX()); System.out.println(point.getY()); } } |
1.4.3 泛型的下限
泛型的下限指的是只能设置其具体的类或者父类,设置语法如下:
? super 类 |
例如,定义一个方法,此方法之鞥接收String或Object类型的泛型对象。
package com.java.demo06; public class Point<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; } } |
在方法中设置泛型的下限:
package com.java.demo06; public class GenDemo14 { public static void main(String[] args) { Point<String> p1 = new Point<String>(); Point<Object> p2 = new Point<Object>(); fun(p1); fun(p2); } public static void fun(Point<? super String> point){ System.out.println(point.getX()); System.out.println(point.getY()); } } |
1.5 泛型接口
泛型不管可以在类上使用,还可以在接口中进行定义。操作的语法如下:
Interface 类口名称<泛型类型,泛型类型,…>{} |
范例:定义泛型接口:
package com.java.demo07; public interface Demo<T> { // 定义泛型接口。 public void print(T param); // 此抽象方法中使用了泛型类型 } |
泛型接口定义完成之后,下面就需要定义类是吸纳接口,实现的方法有两种:
范例:第一种实现手段
package com.java.demo07; public class DemoImpl01<T> implements Demo<T> { public void print(T param) { System.out.println("Param = " + param); } } |
下面对以上的程序进行测试:
package com.java.demo07; public class GenDemo15 { public static void main(String[] args) { Demo<String> demo = new DemoImpl01<String>(); demo.print("Hello"); } } |
范例:第二种做法(设置具体类型)
package com.java.demo07; public class DemoImpl02 implements Demo<DemoImpl02> { // 设置具体类型 public void print(DemoImpl02 param) { System.out.println("Param = " + param); } } |
此时print方法中只能接收DemoImpl02实例对象
package com.java.demo07; public class GenDemo16 { public static void main(String[] args) { Demo<DemoImpl02> demo = new DemoImpl02(); demo.print(new DemoImpl02()); } } |
1.6 泛型方法
泛型除了可以再类中定义之外,还可以在方法中定义,而且在方法上使用泛型,此方法所在的类不一定是泛型的操作。
package com.java.demo08; public class Demo { public <T> T print(T param){ // 定义泛型方法 return param; } } |
Demo类中print()方法可以接收泛型类型,而且此方法的返回值也是指定的泛型类型,下面使用以上的类进行操作。
package com.java.demo08; public class GenDemo17 { public static void main(String[] args) { Demo demo = new Demo(); System.out.println(demo.print(1)); } } |
当然也可以将方法的返回值定义成一个泛型的数组。
package com.java.demo08; public class GenDemo18 { public static void main(String[] args) { Integer i[] = fun(1, 2, 3, 4, 5, 6, 7, 8, 9); for(int x : i){ System.out.print(x + "\t"); } } public static <T> T[] fun(T... param) { return param; // 返回数组 } } |
1.7 泛型的嵌套操作
现在只是突出语法,具体操作的意义要等到后面学习到类库才能够更加明白。
package com.java.demo09; public class Info<T> { private T param; public T getParam() { return param; } public void setParam(T param) { this.param = param; } } |
之后再定义一个Person类
package com.java.demo09; public class Person<T> { private T info; public T getInfo() { return info; } public void setInfo(T info) { this.info = info; } } |
此时如果要将Info的类型设置到Person中,那么同时既要指定Person的泛型类型,又要指定Info中的泛型类型。
package com.java.demo09; public class Test { public static void main(String[] args) { Person<Info<String>> person = new Person<Info<String>>(); person.setInfo(new Info<String>()); person.getInfo().setParam("Hello World!!!"); System.out.println(person.getInfo().getParam()); } } |
这就是泛型的嵌套的设置,以上的在后面将会有所应用。
1.8 泛型的操作范例
现在有如下的题目要求:
要求设计一个程序,定义一个Person类,Person类中药存放具体的信息,但是信息分为基本信息或者联系方式等等,那么此时该如何设计呢?
此时最好的设计就是需要定义一个好事信息的操作标准,但是此时这个标准肯定使用接口实现,但是现在接口中并不编写任何的操作。
package com.java.demo10; public interface Info { } |
接口中没有任何操作代码,所以,此种接口在设计上称为标示接口,标示一种能力。之后定义Person类,Person类中的信息只能由Info的子类决定,所以此时指定了上限。
package com.java.demo10; public class Person<T extends Info> { private T info; public T getInfo() { return info; } public void setInfo(T info) { this.info = info; } } |
以上的操作类中,能够设置的内容只能是Info的子类.
package com.java.demo10; public class Basic implements Info { private String name; private int age; public Basic() { super(); } public Basic(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() { return "人的信息:" + "\n\t|-姓名:" + this.getName() + "\n\t|-年龄:" + this.getAge(); } } |
以上只是基本信息,但是在人中还有联系方式的子类.
package com.java.demo10; public class Contact implements Info { private String address; private String zipcode; public Contact() { super(); } public Contact(String address, String zipcode) { super(); this.address = address; this.zipcode = zipcode; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } public String toString() { return "联系信息:" + "\n\t|-家庭住址:" + this.getAddress() + "\n\t|-邮政编码:" + this.getZipcode(); } } |
下面使用基本信息完成操作:
package com.java.demo10; public class TestPerson01 { public static void main(String[] args) { Person<Basic> person = new Person<Basic>(); person.setInfo(new Basic("张三", 30)); System.out.println(person.getInfo()); } } |
下面使用地址信息完成操作:
package com.java.demo10; public class TestPerson02 { public static void main(String[] args) { Person<Contact> person = new Person<Contact>(); person.setInfo(new Contact("开封市", "475001")); System.out.println(person.getInfo()); } } |
以上Person中的信息只能是Info的子类,从而保证了操作的正确性.