十九、泛型的使用
1、泛型引入背景
Java集合不会知道它们需要用它来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性。就是因为集合对元素类型没有任何限制,这样回引发下列的问题:
例如想创建一个只能保存Order对象的集合,但程序也可以轻易地将User对象“丢”进去,所以可能引发异常。由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道它盛装的是Object,因此取出集合元素后通常还需要进行强制类型转换。这种强制类型转换既会增加编程的复杂度、也可能引发ClassCastException。
因此JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。
2、泛型含义:就是允许在定义类、接口指定类型形参,这个类型形参将在声明变量、创建对象时确定(即传入实际的类型参数,也可称为类型实参)。泛型的创建、调用:
1)为创建的泛型类定义的构造器时,构造器名还是原来的类名,不要增加泛型声明。例如,为 GeneralClass<T>类定义构造器时,其构造器还是 GeneralClass,而不是 GeneralClass<T>。
2)调用构造器时,却可以使用GeneralClass<T>的形式,当然应该为T形参传入实际的类型参数。Java 7提供了菱形语法,允许省略<>中的类型实参。
泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型。泛型可以用于类、接口、方法,通过使用泛型可以使代码更简单、安全。然而 Java 中的泛型使用了类型擦除,所以只是伪泛型。
3、泛型机制必要性及原理解析
泛型程序设计(Generic Programming):“使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。泛型对于集合类尤其有用。”
4、Java泛型中的标记符含义:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
5、通配符(Wildcards)可以在泛型实例化时更加灵活地控制,也可以在方法中控制方法的参数。
语法如下:
示例1:泛型类名<? extends T>
? extends T:表示T或T的子类
示列2:泛型类名<? super T>
? super T:表示T或T的父类
示例3:泛型类名<?>
?:表示可以是任意类型
6、泛型的具体应用:
6.1泛型类的定义:[访问修饰符] class 类名称<T>
泛型类的主要作用在于类被实例化后,传入具体的类型参数,对类的成员属性的类型和成员方法的参数类型和返回值类型进行替换。
1)创建的泛型类,为该类定义的构造器时,构造器名还是原来的类名,不要增加泛型声明。例如,为 GeneralClass<T>类定义构造器时,其构造器还是 GeneralClass,而不是 GeneralClass<T>。
2)调用构造器时,却可以使用 GeneralClass<T>的形式,当然应该为T形参传入实际的类型参数。Java 7提供了菱形语法,允许省略<>中的类型实参。代码示例如下:
public class GeneralClass<T> { T data; GeneralClass(T t) { data = t; } public T getData() { return data; } public void print() { System.out.println("base print : " + data); } public static void main(String[] args) { GeneralClass<String> generic1 = new GeneralClass<>("测试1"); System.out.println(generic1.getData()); GeneralClass generic2 = new GeneralClass<>("测试2"); System.out.println(generic2.getData()); GeneralClass<Double> generic3 = new GeneralClass<Double>(9999.7); System.out.println(generic3.getData()); AnimalDTO animalDTO=new AnimalDTO(); animalDTO.setUserName("动物"); GeneralClass generic4=new GeneralClass(animalDTO); System.out.println(generic4.getData()); } }
6.2泛型方法定义:[public] [static] <T> 返回值类型 方法名(T 参数列表)
使用泛型方法时,至少返回值或参数有一个是泛型定义的,而且应该保持一致,否则可能会受到各种限制,因此,这里建议保持一致。代码示例如下:
public class GeneralMethod { public static void main(String[] args) { GeneralMethod generalMethod= new GeneralMethod(); generalMethod.getClassSimpleName(new Integer(0),new String("generic")); } /** * @Method: * @Author: * @Description: 泛型方法 * GeneralMethod 类本身不是泛型的,创建它的对象的时候不需要传入泛型参数,但是它的方法 getClassSimpleName 是泛型方法。在返回类型之前是它的参数标识 <K,V>,注意这里有两个泛型参数,所以泛型参数可以有多个。调用泛型方法时可以不显式传入泛型参数,上面的调用就没有。这是因为编译器会使用参数类型推断,根据传入的实参的类型 (这里是 integer 和 String) 推断出 K 和 V 的类型。 * * param: * @Return: * @Exception: * @Date: 2020/12/9 14:51 */ public <K,V> void getClassSimpleName(K k,V v) { System.out.println(k.getClass().getSimpleName()); System.out.println(v.getClass().getSimpleName()); } }
6.3泛型类的继承:代码示例如下
public class Father<E> { E e; } public class Son<T,T1> extends Father<T>{ /* 子类Son在定义的时候,如果省略了Father后<T>,那么Son的T自动变成了Object,建议定义时加入<T>以保留父类的类型参数。Son类还可以增加新的泛型T1。 */ public static void main(String[] args){ System.out.println("Son>>>执行"); } }
6.4通配符在泛型中的使用:代码示例如下
public class WildcardsTest2 { public static void main(String[] args) { List<? super Class1> list = new ArrayList<Class1>(); // 对于List<? super Class1> list,可以指向ArrayList<T1>和ArrayList<Class1>的引用,但是进行add操作时,只能添加Class1和Class2 list.add(new Class1()); list.add(new Class2()); List<BaseClass> list1 = new ArrayList<BaseClass>(); List<Class1> list2 = new ArrayList<Class1>(); List<Class2> list3 = new ArrayList<Class2>(); f(list1); f(list2); f(list3); } //只要是class2 的父类都可以 public static void f(List<? super Class2> list) { System.out.println("list>>>" + list); } } class BaseClass { } class Class1 extends BaseClass { } class Class2 extends Class1 { }
7、代码开发中使用泛型,创建公共返回对象
public class ResponseDTO<T> implements Serializable { private boolean success; private String errorCode; /** * 原因 */ private String errorMsg; /** * 返回数据值 */ private T data; public ResponseDTO() { } public ResponseDTO(ErrorCodeEnum errorCode) { this.errorCode = errorCode.getErrorCode(); this.errorMsg = errorCode.getErrorMsg(); if(!"0".equals(errorCode.getErrorCode())){ success=false; }else{ success=true; } } public ResponseDTO(String errorCode, String errorMsg) { this.errorCode = errorCode; this.errorMsg = errorMsg; if(!"0".equals(errorCode)){ success=false; }else{ success=true; } } public void setErrorCodeEnum(BaseEnum errorCode) { this.errorCode = errorCode.getErrorCode(); this.errorMsg = errorCode.getErrorMsg(); if(!"0".equals(errorCode.getErrorCode())){ success=false; }else{ success=true; } } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public T getData() { return data; } public void setData(T data) { this.data = data; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } @Override public String toString() { return "ResponseDTO{" + "errorCode='" + errorCode + '\'' + ", errorMsg='" + errorMsg + '\'' + ", data=" + data + '}'; } } public interface BaseEnum { public String getErrorCode(); public String getErrorMsg(); } public enum ErrorCodeEnum implements BaseEnum{ OK("0", "成功"), FAIL_500("500", "系统开小差了,请稍后再试!"), FAIL_501("501", "服务异常,请联系管理员处理!"), PARAM_ERROR("502", "入参异常,请检查后重试!"); private String errorCode; private String errorMsg; ErrorCodeEnum(String errorCode, String errorMsg) { this.errorCode = errorCode; this.errorMsg = errorMsg; } ErrorCodeEnum(BaseEnum errorCodeEnum) { this.errorCode = errorCodeEnum.getErrorCode(); this.errorMsg = errorCodeEnum.getErrorMsg(); } public String getErrorCode() { return errorCode; } public String getErrorMsg() { return errorMsg; } }
参看文章
https://www.jb51.net/article/109033.htm
https://www.jb51.net/article/166703.htm