泛型
从JDK1.5之后引入了泛型,泛型是一件非常重要的实现技术,他可以帮助我们解决程序的参数转换问题。
假如需要定义一个描述坐标的程序类Point,需要提供两个属性x,y。对于这两个属性的内容可能有如下选择:
1丶int类型:x = 10,y = 20;
2丶double类型:x = 120.2,y = 20.1;
3丶String类型:x = 东经120度,y = 北纬38度;
上面有3种x,y属性类型,需要保存int,double,String,所以只能用一种类型保存所有类型:Object型,
代码如下:
package com.github.generic;
/**
* Created with IntelliJ IDEA.
* Description:
* User: wang
* Date: 2019-05-06
* Time: 23:43
**/
public 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;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
接下来分别设置各种类型的坐标:
public static void main(String[] args) {
{
Point intPoint = new Point();
intPoint.setX(10);
intPoint.setY(20);
int x = (int) intPoint.getX();
int y = (int) intPoint.getY();
System.out.println("Point(x,y)=(" + x + "," + y + ")");
}
{
Point strPoint = new Point();
strPoint.setX("东经120度");
strPoint.setY("北纬38度");
String x = (String) strPoint.getX();
String y = (String) strPoint.getY();
System.out.println("Point(x,y)=(" + x + "," + y + ")");
}
以上两种x,y属性类型相同,所以并不会产生错误,结果如下:
但是在设置时,可能会出现错误,比如讲坐标内容设置成String和double,但是接收方并不知道,此时就会出现CCE(ClassCastException):指的是两个没有关系的对象进行强转出现的异常,代码编译通过,但是在运行时会报异常
代码如下:
{
//编译通过
//运行异常
Point point = new Point();
point.setX(120.2);//double
point.setY("北纬38度");//String
printPoint(point);
}
public static void printPoint(Point strPoint){
String x = (String) strPoint.getX();
String y = (String) strPoint.getY();
System.out.println("Point(x,y)=(" + x + "," + y +")");
}
此时会出现CCE异常,结果如下:
所以为例避免发生CCE,我们需要使用泛型,泛型分为泛型类和泛型方法,下面先来看泛型类:
一丶泛型类
1.定义一个类属性和方法参数通过类型参数定义,在使用时明确具体类型
2.className<T,[…]>{T x, T y}
3.避免向下转型带来的CCE问题,编译时检查(属性或者方法调用的赋值)
4.使用方式:ClassName<类型参数的具体类型> var = new ClassName<>(…)
代码如下:
package com.github.generic;
/**
* Created with IntelliJ IDEA.
* Description:
* User: wang
* Date: 2019-05-08
* Time: 17:29
**/
public class Point1<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;
}
@Override
public String toString() {
return "Point1{" +
"x=" + x +
", y=" + y +
'}';
}
}
注意:泛型只能接受类,所有的基本数据类型必须使用包装类
public class TestPoint1 {
public static void main(String[] args) {
Point1<Integer> intPoint1 = new Point1<>();
// intPoint1.setX(10);
// intPoint1.setY("北纬38度");//这里会发生错误
// int x = intPoint1.getX();
// int y = intPoint1.getY();
Point1<String> stringPoint1 = new Point1<>();
stringPoint1.setX("东经120度");
stringPoint1.setY("北纬38度");
System.out.println(stringPoint1.getX()+" "+stringPoint1.getY());
}
}
结果如下:
上面是用泛型定义一种属性类型,当然也可以定义多种类型,
比如定义x为int,定义y为String,
代码如下:
public class Point2<T,E>{
private T x;
private E y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public E getY() {
return y;
}
public void setY(E y) {
this.y = y;
}
@Override
public String toString() {
return "Point2{" +
"x=" + x +
", y=" + y +
'}';
}
}
public class TestPoint2 {
public static void main(String[] args) {
Point2<Integer,String> point2 = new Point2<>();
point2.setX(10);
point2.setY("北纬38度");
Integer x = point2.getX();
String y = point2.getY();
System.out.println("("+ x + ","+ y +")");
}
}
也可定义两种相同的类型,x,y都为int
代码如下:
Point2<Integer,Integer> point2 = new Point2<>();
point2.setX(10);
point2.setY(20);
Integer x = point2.getX();
Integer y = point2.getY();
System.out.println("("+ x + ","+ y +")");
}
}
具体需要用什么数据类型,可以由接收方自己定义,这就是泛型类的好处。
下面再来看泛型方法:
二丶泛型方法
1.定义一个方法方法参数通过类型参数定义,在使用时明确参数类型
2.<T,[…]> 返回值类型 methodName(参数列表) eg: void print(T t)
3.泛型方法和泛型类之间是独立的,各自使用自定义的类型参数的具体类型
4.方法中使用泛型类作为参数类型,可以使用?通配符,表示类型参数具体类型是任意类型,但是在方法中不能修改参数值
泛型方法和泛型类可以共存
代码如下:
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 <T> void printPoint(T t){
System.out.println(t);
}
public <T,E> void printPoint(T t,E e){
//处理业务
System.out.println(e);
System.out.println(t);
}
@Override
public String toString() {
return "Point3{" +
"x=" + x +
", y=" + y +
'}';
}
}
public class TestPoint3 {
public static void main(String[] args) {
// Point3 point3 = new Point3();
// Point2<Integer,Double> point2 = new Point2<>();
// point2.setX(10);
// point2.setY(20.5D);
// //T -> Point
// point3.setX(10);
// point3.setY(20);
// point3.printPoint("Hello World");
//
Point3<Integer> point3 = new Point3<>();
point3.setX(10);
point3.setY(20);
point3.printPoint("Hello World");
}
}
通配符
在前面用泛型避免了CCE问题,但是又会产生新的情况:参数的统一问题,我们需要可以接收所有的泛型类型,但是又不能让用户随意修改,所以需要使用通配符“ ?” 来处理
代码如下:
public class Message<T> {
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
@Override
public String toString() {
return "Message{" +
"message=" + message +
'}';
}
}
public class TestMessage {
//Message泛型类的类型参数的具体类型String
// public static void fun(Message<String> message) {
// System.out.println(message.getMessage());
// }
//fun方法接受的Message对象的类型参数具体类型是任意类型
public static void fun(Message<?> message){
// message.setMessage(111);
System.out.println(message.getMessage());
}
public static void main(String[] args) {
Message<String> message = new Message<>();
message.setMessage("王泽明");
fun(message);
Message<Integer> message1 = new Message<>();
message1.setMessage(666);
fun(message1);
}
}