1、泛型的引入
假设现在有一个坐标类Point,有两个属性,x坐标和y坐标;x y取值共有三种情况
case1:x=10.1 y=18.9(double)
case2:东经101度,北纬30度(字符串)
case3:x=10 y=49(整型)
Point{
x;
y;
}
此时问题就来了,定义x y时应该定义为什么类型?
我们都知道Object类是所有类的父类,因此可以接受所有类型,有包装类的自动拆装箱,基本类型自动装箱变为Integer或Double让Objext类接收。
// 坐标类
public class Point {
private Object x;
private Object y;
public Object getX() {
return x;
}
public Object getY() {
return y;
}
public void setX(Object x) {
this.x = x;
}
public void setY(Object y) {
this.y = y;
}
public static void main(String[] args) {
Point point=new Point();
point.setX(10.2); // 自动装箱,把double类型变为包装类的double类型,用Object接收
point.setY(12.3);
// 自动拆箱
double x=(double) point.getX();
double y=(double) point.getY();
System.out.println("x = " + x + " y = " + y );
Point point1=new Point();
// 自动拆箱
point1.setX("东经101度");
point1.setY("北纬30度");
String x1=(String) point1.getX();
String y1=(String) point1.getY();
System.out.println("x = " + x1 + " y = " + y1);
}
}
运行结果
x = 10.2 y = 12.3
x = 东经101度 y = 北纬30度
此时可以接收不同类型,但依然存在风险,自动拆箱时的强制类型转换。x y都是相同的类型,若用户不小心输入的x和y 类型不同时,就会在强转时发生错误。
package java1_22_inner_and_rawtype;
// 坐标类
public class Point {
private Object x;
private Object y;
public Object getX() {
return x;
}
public Object getY() {
return y;
}
public void setX(Object x) {
this.x = x;
}
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度");
String x=(String) point.getX();
String y=(String) point.getY();
System.out.println("x = " + x + " y = " + y);
}
}
运行结果
2、泛型定义
泛型:类定义时不会设置类中属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。
泛型的基本使用:守门员,在编译期检查错误。
类声明后的<>中的T被称为类型参数,用于指代任意类型,实际上这个T只是个代表,写啥都行。表示此时value1, value2都是在类定义时没有明确类型,只有在使用时才告知编译器类型。就像定义坐标x,y 时,并不知道x.y 是什么类型,赋值使用才能明确类型。出于规范而言,类型参数用单个的大写字母来代替。常见的如下:
T :代表任意类
E:表示Element的意思,或是异常
K:与V搭配使用,Map<Integer,String>
V:
// 泛型类
public class Generic<T> {
T value1;
T value2;
public void setValue1(T value1) {
this.value1 = value1;
}
public void setValue2(T value2) {
this.value2 = value2;
}
public T getValue1() {
return value1;
}
public T getValue2() {
return value2;
}
}
当类型明确后,赋值为其他类型,编译器就会报错,这也是泛型的意义。
若value1与value2类型不同,可定义多个类型参数
// 泛型类
// T和E表示两种类型参数,在具体使用时可以相同也可以不同
public class Generic<T,E> {
T value1;
E value2;
public static void main(String[] args) {
// 创建这个泛型类的时候才明确类型为整型
Generic<Integer,String> generic=new Generic<>();
generic.value1=10;
generic.value2="123";
Generic<Integer,Integer> generic1=new Generic<>();
generic1.value1=10;
generic1.value2=30;
}
}
使用泛型改造Point类
package java1_22_inner_and_rawtype;
// 泛型坐标类
public class GenericPoint<T> {
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) {
GenericPoint<String> genericPoint=new GenericPoint<>();
genericPoint.setX("北纬20度");
genericPoint.setY(10);
}
}
当创建GenericPoint对象时设置的类型与实际存储类型不同时,编译就会报错,提前将问题暴露。
泛型不光可使问题提前暴露。还可以不用强制类型转换。
public static void main(String[] args) {
GenericPoint<String> genericPoint=new GenericPoint<>();
genericPoint.setX("北纬20度");
genericPoint.setY("东京49度");
// 不需要强制类型转换
String x= genericPoint.getX();
String y= genericPoint.getY();
}
3、泛型方法
此处的泛型方法指的是有自己的类型参数
之前的这个不是泛型方法,只不过是返回值或参数使用了泛型变量而已,此时T是泛型类的参数
public void setY(T y) {
this.y = y;
}
public T getY() {
return y;
}
泛型方法
package java1_22_inner_and_rawtype;
public class GenericMethod<T,E> {
T value1;
E value2;
// 泛型方法
public <T> void test(T t){
System.out.println(t);
}
public static void main(String[] args) {
}
}
泛型方法始终以自己的类型参数为准,与类中类型参数无关,为了避免混淆,定义泛型方法时,尽量避免使用类中使用过的类型参数字母。
package java1_22_inner_and_rawtype;
public class GenericMethod<T,E> {
T value1;
E value2;
// 泛型方法
public <S> void test(S t){
System.out.println(t);
}
public static void main(String[] args) {
GenericMethod<Integer,String> genericMethod=new GenericMethod<>();
genericMethod.value1=10;
// genericMethod.value1="123";
genericMethod.test("123");
}
}
泛型方法有返回值时
// 泛型方法
public <S> S test(S t){
System.out.println(t);
return t;
}