泛型从JDK1.5之后追加到java语言中,其主要目的是为了解决ClassCastException问题,在对对象进行上下转型时都可能存在有安全隐患。通过泛型可以解决一部分这种问题。
泛型问题引出
写一个类,用来记录某个坐标信息(属性为X和Y)。
package chenfu.type.domain;
/**
* @Author: romantic_ke@163.com
* @Description:
* @Date: 2019/5/16 15:43
*/
public class Point {
private Object x;
private Object y;
public Point(Object x, Object y) {
this.x = x;
this.y = 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 +
'}';
}
}
测试类
package chenfu.type;
import chenfu.type.domain.Point;
/**
* @Author: romantic_ke@163.com
* @Description:
* @Date: 2019/5/16 15:50
*/
public class TypeTest {
public static void main(String[] args) {
Point point = new Point(20, 50);
Object x = point.getX();
Object y = point.getY();
System.out.println("x=" + x);
System.out.println("y=" + y);
}
}
上面的代码没有任何问题,通过在Point类中定义x和y属性类型为Object来实现输入整形,浮点型,字符串型数据,但是这也引发了一个问题。如果有需求只让我们在x和y属性中处理整数怎么办?例如如下:
public static void main(String[] args) {
// 此时,就是在输入坐标时,输入字符串也合法
Point point = new Point(20, "OK");
Integer x = (Integer) point.getX();
Integer y = (Integer) point.getY();
System.out.println("x=" + x);
System.out.println("y=" + y);
}
这时就会引发一个ClassCastException异常,但是编译器又不会给我们提示错误,在大型项目编写过程中,这就很头疼了。泛型就是为了解决此类问题,尽量减少运行过程中代码中潜藏的ClassCastException,将其在编译阶段暴露出来。
利用泛型来改造上述代码
泛型用T标识,代表Type。用A,B,C也可以。
package chenfu.type.domain;
/**
* @Author: romantic_ke@163.com
* @Description:
* @Date: 2019/5/16 15:43
*/
public class Point<T> {
private T x;
private T y;
public Point(T x, T y) {
this.x = x;
this.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;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
此时的测试方法,Point的实例化就不能和以前一样了,需要加入属性的具体类型,而且,传入的参数如果不符合给定的数据类型,会在编译时提示错误。
// 此时,正常输出
public static void main(String[] args) {
Point<Integer> point = new Point(20, 50);
Integer x = point.getX();
Integer y = point.getY();
System.out.println("x=" + x);
System.out.println("y=" + y);
}
泛型通配符
问题引出
观察下面代码和输出
public static void main(String[] args) {
Point<Integer> point = new Point(20, 50);
Integer x = point.getX();
Integer y = point.getY();
System.out.println("x=" + x);
System.out.println("y=" + y);
run(point);
}
public static void run(Point point) {
point.setX("234");
System.out.println(point);
}
结果:
x=20
y=50
Point{x=234, y=50}
结论:可以发现,Point类的x属性被修改了,而且被修改为了String类型。如果我们不希望我们的对象数据被修改,那么该如何实现?
通过如下方式,在方法传参的时候,不指定具体类型(使用通配符“?”)。
修改测试方法
public static void run(Point<?> point) {
// 编译时报错
point.setX(1200);
// 编译时报错
point.setY(1200);
String sout = String.format("x=%d,y=%d", point.getX(), point.getY());
System.out.println(sout);
}
泛型的通配符有两种
- ? extends 类:泛型上限-实例化时传入的数据类型必须是某个类或者是这个类的子类
- ? super 类:泛型下限-实例化时传入的数据类型必须是这个类或者这个类的超类(父类)
展示一种泛型上限的使用方式,下限同理。
修改实体类
public class Point<T extends Number> {
测试代码:
public static void main(String[] args) {
Point<Integer> point = new Point<>(20, 50);
Point<Float> point2 = new Point<>(20.3f, 50.3f);
Point<Double> point3 = new Point<>(100d, 50.3d);
run(point);
run(point2);
run(point3);
}
public static void run(Point<? extends Number> point) {
System.out.println(point);
}
输出:
Point{x=20, y=50}
Point{x=20.3, y=50.3}
Point{x=100.0, y=50.3}
总结:泛型通配符能让我们的数据更加安全(不被别的方法所修改),通过上限和下限的设置也能避免很多的数据转型的异常。
泛型接口
定义泛型接口
public interface TypeA<T> {
void setT(T t);
T getT();
}
实现该接口的类如果也是泛型类,直接实现接口即可。如果不是泛型类,需要在接口上注明实际类型
class TypeB<T> implements TypeA<T> {
@Override
public void setT(T t) {
}
@Override
public T getT() {
return null;
}
}
class TypeC implements TypeA<Integer>{
@Override
public void setT(Integer integer) {
}
@Override
public Integer getT() {
return null;
}
}
如果是某个接口继承此接口,也是如此
interface TypeB<T> extends TypeA<T> {
}
interface TypeC extends TypeA<Integer> {
}
总结:某个类或接口继承或实现了泛型接口,就务必要对其泛型进行指定,除非实现或继承了该泛型接口的类或接口也是泛型类或泛型接口。
泛型方法
泛型方法在实际开发中经常出现在工具类中,主要是对一些集合或者数据进行一些非空的校验。定义和使用起来也是非常的方便。如果需要返回值,需要在返回泛型的同时在其前面加上"<T>"对返回的数据类型进行补充和说明。
事例如下:
package chenfu.type.function;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
/**
* @Author: romantic_ke@163.com
* @Description:
* @Date: 2019/5/16 17:19
*/
public class TypeFuntions {
static class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
public static <T> T run(T t) throws Exception {
if (Objects.nonNull(t) && (t instanceof List)) {
List list = (List) t;
if (!list.isEmpty() && list.iterator().hasNext()) {
return t;
}
}
throw new MyException("不是List或者List没数据");
}
public static void main(String[] args) throws Exception {
// ArrayList<Object> list = new ArrayList<>();
HashSet<Object> objects = new HashSet<>();
// run(list);
run(objects);
}
}
代码地址:https://github.com/chenfu1201/java.git
本案例所有代码均在该项目的type目录下