1.泛型方法和泛型参数的介绍
在Java中,泛型方法和泛型参数是一种强大的特性,它们可以增加代码的灵活性和可重用性。下面是对泛型方法和泛型参数的介绍:
-
泛型方法:
泛型方法是一种在方法中使用泛型类型的方式。通过在方法声明中使用尖括号和类型参数,可以使方法具有通用性,可以在调用时指定具体的类型。泛型方法可以在返回类型之前使用类型参数,也可以在参数列表中使用类型参数。例如,下面是一个简单的泛型方法示例:
public <T> void printArray(T[] array) { for (T element : array) { System.out.println(element); } }
在上面的示例中,
<T>
表示类型参数,T[]
表示接受一个泛型数组作为参数。在调用该方法时,可以传入任意类型的数组。上面的方法为没有返回结果的,也可以定义有返回结果的泛型参数,其中的返回参数的类型可以与传入的参数产生关系。
在Java中,方法可以返回泛型类型。这样的方法可以在调用时指定具体的类型参数,从而实现灵活的类型处理。以下是一种常见的方法返回泛型的方式:
public <T> T getGenericValue(T value) { // 在这里可以对value进行一些处理 return value; }
在上述代码中,
<T>
表示这是一个泛型方法,T
是类型参数。方法的返回类型是T
,即根据调用时传入的类型参数决定返回的具体类型。使用该方法时,可以根据需要传入不同的类型参数,例如:
String stringValue = getGenericValue("Hello"); Integer intValue = getGenericValue(123);
在上述示例中,第一次调用
getGenericValue
方法时,传入的类型参数是String
,所以返回值的类型是String
;第二次调用时传入的类型参数是Integer
,所以返回值的类型是Integer
。需要注意的是,泛型方法可以有多个类型参数,并且可以在方法参数、返回值、局部变量等位置使用这些类型参数。
-
泛型参数:
泛型参数是一种在类、接口或方法中使用的类型参数。通过在类、接口或方法声明中使用尖括号和类型参数,可以使其具有通用性,可以在实例化或调用时指定具体的类型。例如,下面是一个简单的泛型类示例:
public class Box<T> { private T item; public void setItem(T item) { this.item = item; } public T getItem() { return item; } }
在上面的示例中,
<T>
表示类型参数,Box<T>
表示一个泛型类。在实例化该类时,可以指定具体的类型,例如Box<Integer>
或Box<String>
。
2.实战应用
场景介绍:
这里想提供一个Util工具类的一个工具方法,用于获取任何对象的某个属性的值,先上改造后的代码:
优化后代码
说明:
这个方法里的内部逻辑还不够完善,请忽略这里面的内部取值逻辑缺陷,主要介绍泛型的使用。
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
public class SyncUtil {
/**
* 根据属性名称和类型获取对象对应的属性值
* @param obj 需要获取属性的对象
* @param propName 属性名
* @param resultType 希望返回的类型
* @return
*/
public static <T> T getValueByPropertyName(Object obj, String propName, Class<T> resultType) {
MetaObject meta = SystemMetaObject.forObject(obj);
if (meta.hasGetter(propName)) {
System.out.println(meta.getGetterType(propName).getName());
if (meta.getValue(propName) instanceof Integer) {
if (resultType.isAssignableFrom(Long.class)) {
return (T) Long.valueOf((Integer) meta.getValue(propName));
}
} else if (meta.getValue(propName) instanceof Long) {
if (resultType.isAssignableFrom(Integer.class)) {
return (T) (Integer.valueOf(Math.toIntExact((Long) meta.getValue(propName))));
}
}
if(resultType.isAssignableFrom(String.class)){
if(meta.getValue(propName) == null ){
return null;
}else{
return (T) meta.getValue(propName).toString();
}
}
} else {
throw new IllegalArgumentException(obj.getClass() + "中没有属性" + propName + "的get方法");
}
return (T)meta.getValue(propName);
}
}
这里有三个参数,分别是obj
需要获取属性的对象,propName
属性名,resultType
希望返回的类型。
其中resultType
定义的类型为 Class<T>
,这里需要返回的结果类型就是传入的入参类型的class。下面看看调用的地方:
SaveBudgetCloseCaseReq req = SaveBudgetCloseCaseReq.builder().applyId(emsApplyId)
.orgId(getValueByPropertyName(head, "orgId", Long.class))
.deptId(getValueByPropertyName(head, "deptId", Long.class))
.deptName(getValueByPropertyName(head, "deptName", String.class))
.personId(getValueByPropertyName(head, "personId", Long.class))
.personName(getValueByPropertyName(head, "personName", String.class))
.positionId(getValueByPropertyName(head, "positionId", Long.class))
.positionName(getValueByPropertyName(head, "positionName", String.class))
.sourceBillNo(getValueByPropertyName(head, "billNo", String.class))
.sourceBillSystem(StoreRebateConstants.Ems2Code.EMS2_SOURCE_BILL_SYSTEM)
.userId(getValueByPropertyName(head, "userId", Long.class))
.closeCaseLines(lines).build();
从上面可以看出,获取后的结果就是需要返回的结果,不需要再做转换,可以省了很多事,而且代码看起来简介。
下面看看优化前的代码。
优化前代码
/**
* 根据属性名称和类型获取对象对应的属性值
* @param obj 需要获取属性的对象
* @param propName 属性名
* @param resultType 希望返回的类型
* @return
*/
public static Object getValueByPropertyName(Object obj, String propName, Class<?> resultType) {
MetaObject meta = SystemMetaObject.forObject(obj);
if (meta.hasGetter(propName)) {
System.out.println(meta.getGetterType(propName).getName());
if (meta.getValue(propName) instanceof Integer) {
if (resultType.isAssignableFrom(Long.class)) {
return Long.valueOf((Integer) meta.getValue(propName));
}
} else if (meta.getValue(propName) instanceof Long) {
if (resultType.isAssignableFrom(Integer.class)) {
return Math.toIntExact((Long) meta.getValue(propName));
}
}
} else {
throw new IllegalArgumentException(obj.getClass() + "中没有属性" + propName + "的get方法");
}
return meta.getValue(propName);
}
这里返回的类型为Object类型,获取的时候是需要去强转的,而在强转的时候可能出现异常,而且写起来很啰嗦,很繁琐。
下面看下调用的地方:
public static SaveBudgetCloseCaseReq buildEmsCloseCaseReq(Object head, List<SaveBudgetCloseCaseLineReq> lines) {
SaveBudgetCloseCaseReq req = SaveBudgetCloseCaseReq.builder().closeCaseHeadId((Long) getValueByPropertyName(head, "closeCaseHeadId", Long.class))
.orgId((Long) getValueByPropertyName(head, "orgId", Long.class))
.deptId((Long) getValueByPropertyName(head, "deptId", Long.class))
.deptName((String) getValueByPropertyName(head, "deptName", String.class))
.personId((Long) getValueByPropertyName(head, "personId", Long.class))
.personName((String) getValueByPropertyName(head, "personName", String.class))
.positionId((Long) getValueByPropertyName(head, "positionId", Long.class))
.positionName((String) getValueByPropertyName(head, "positionName", String.class))
.sourceBillNo((String) getValueByPropertyName(head, "billNo", String.class))
.sourceBillSystem(StoreRebateConstants.Ems2Code.EMS2_SOURCE_BILL_SYSTEM)
.userId((Long) getValueByPropertyName(head, "userId", Long.class))
.closeCaseLines(lines).build();
req.setCloseCaseLines(lines);
return req;
}
可以看出,获取到结果后,都是做了强转的。
3.总结
使用泛型的好处:
1.提升了程序的健壮性和规范性
2.编译时,检查添加元素的类型,提高了安全性
3.减少了类型转换的次数,提高效率