目录
引例
泛型概念
泛型方法
泛型接口
当我们要创建一个坐标类Point,输入经度和纬度时,要将它定义为什么类型呢?
- 10.1,30.2 —都是double类型
- 东经100度,北纬38度—都是字符串
- 10 , 50—都是整型
Object 可以接收所有类型,这时我们能想到使用包装类的自动拆装箱,基本类型自动装箱变为Integer或者Object接收
代码示例:
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;
}
public static void main(String[] args) {
Point point = new Point();
//自动装箱
point.setX(10.1);
point.setY(30.2);
//自动拆箱
double x1 = (double) point.getX();
double y1 = (double) point.getY();
System.out.println("x="+x1+","+"y="+y1);
Point point1 = new Point();
point1.setX("东经100度");
point1.setY("北纬38度");
String x2 = (String) point1.getX();
String y2 = (String) point1.getY();
System.out.println("x="+x2+","+"y="+y2);
}
}
//输出:
x=10.1,y=30.2
x=东经100度,y=北纬38度
上述代码中x和y都是相同的类型,若用户输入时不小心输入了不同的类型,那么在强制转换时就会发生错误。如下图:
所谓泛型就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时,也就是在创建对象时再进行类型的定义。
示例:
public class MyClass {
T val1;
T val2;
}
可以看到MyClass类后面加了一个
出于规范通常用的有以下几个:
- T 代表任意类
- E 表示 Element或者异常
- K 通常和 V 搭配使用,Map< Key, Value >
此时我们来创建对象:
public class MyClass {
T val1;
T val2;
public static void main(String[] args) {
MyClass myClass = new MyClass<>();
myClass.val1 = 10;
myClass.val2 = 12;
}
}
可以看到,在创建对象时我们确定了使用整型,若不小心给对象赋了其他类型的值就会报错,这就是泛型的作用。
若val1 和 val2 的类型不一定相同,就可以定义多个类型参数,在具体使用时可以相同也可以不同
public class MyClass {
T val1;
E val2;
public static void main(String[] args) {
MyClass myClass = new MyClass<>();
myClass.val1 = 10;
myClass.val2 = "嘻嘻哈哈";
}
}
public class MyClass {
T val1;
E val2;
public static void main(String[] args) {
MyClass myClass = new MyClass<>();
myClass.val1 = 10;
myClass.val2 = 123;
}
}
回到我们最初引例的Point坐标类,可以将代码做如下修改:
//泛型坐标类
public class Point {
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
public static void main(String[] args) {
Point point = new Point<>();
point.setX("北纬20度");
point.setY("东经120度");
}
}
显然使用泛型之后代码根据简单不易出错。当创建对象时设置的类型和实际存储类型不同时,编辑就会报错,程序无法启动,这就是一种保护,将问题提前的暴露了出来。 而且泛型不光是在编译期就会校验类型,使用它时还不需要强制类型转换:
因为getX方法的返回值已经由T变成了String类型,所以直接使用就行,不需强制转换。
泛型方法始终以自己的类型参数为准,而且可以独立于泛型类存在,普通类中也可以。
上述例子中的 setY和 getX不是泛型方法,只是返回值或者参数使用了泛型变量而已
真正的泛型方法是什么样呢?
图中的test方法就是一个泛型方法,其中 void 前面的< T > 不是返回值,只是告知编译期这是一个泛型的声明,void 就是指该方法没有返回值
方法的泛型和类中的泛型是两回事儿,泛型方法始终以自己的参数为准,与类中的类型参数无关,为避免混淆,一般定义泛型方法时,尽量避免使用类中使用过的类型参数字母。如下图,类对象为整型,而test方法使用了字符串类型,代码可以正常运行。
public interface IPick {
//注意getPick不是泛型方法
T getPick(T t);
}
class FindImpl implements IPick{
@Override
public T getPick(T t) {
return null;
}
}
class FindImpl2 implements IPick{
@Override
public String getPick(String s) {
return null;
}
}
可以看到,子类在实现接口时有两种选择,要么继续保留泛型,要么定义时直接明确类型
这里它调用eat方法时报错了,可Object不是所有类的父类吗,这里应该是发生了向上转型呀,为什么报错呢?一定要记住,使用 < > 括起来的是泛型,泛型没有向上转型,必须是相同的类型才能通过校验