我正在尝试为JDBC创建一个通用的行映射器。
我最初的尝试看起来像:
public class GenericRowMapper<T> implements RowMapper<T> {
private final Class<?> clazz;
public GenericRowMapper(T instance) {
this.clazz = instance.getClass().getComponentType();
}
@Override
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
Field[] fields = clazz.getDeclaredFields();
for(Field f : fields){
System.out.println("field name : " + f.getName() + " , type : " + f.getType());
}
try {
return (T) clazz.getConstructor().newInstance();
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
但是在构造函数中,getComponentType()返回null,因此clazz = null;
和我无法取得任何进展。
我做错了什么?
我做错了什么?
许多新手程序员都会犯的错误(一些专家也会犯!)。
读取文档失败.
如果这样做了,您可能已经注意到getComponentType()
的文档告诉您它返回数组的组件类型。
给定Class<?> c = String[].class
,我可以调用c.getComponentType()
并返回String.class
。或者new String[10].getClass().getComponentType()
也会得到相同的结果
你显然是在一个不代表数组类型的类上调用.getComponentType()
,因此,你得到了null
。
做对了
你的代码很混乱。T
是一个类型变量,因此,接受T instance
的参数意味着你必须提供它的实际实例,即,如果你有一个RowMapper
,将一行映射到字符串,你必须提供一个字符串,这是没有意义的。假设你真正想要的arg是Class<T>
,在这种情况下,瞧,这就是你的类。一开始就不需要对它调用.getClass()
或.getComponentType()
。如果你真的打算让这个方法要求你首先传递一个实际的实例,.getClass()
将是你可能想要的方法,除了我不认为这会起作用:.getClass()
总是给你实际的类型,即在类型列表的变量上调用.getClass()
永远不能返回List.class
-它可能会返回ArrayList.class
或一些实现列表的匿名内部类。
其余的代码同样令人困惑
.getDeclaredFields
()只返回类定义本身的字段,而不是任何父类(父类的字段与其他类的任何实例一样多)—再次,请阅读文档以了解这一点。
'print a stack trace, return null'是非常愚蠢的异常处理。正确的"我不(还)关心它"异常处理程序是throw new RuntimeException("unhandled", e);
-更短,更好。没有理由不这样做。修改IDE的模板
不支持现代java
许多,许多现代的java(特别是包括record
功能)有一个不可变的类,因此你不能只是"设置"一堆字段,也不会有一个无参数的构造函数(.getConstructor()
让你,因此,.getConstructor()
会失败)。相反,构造函数本身接受所有参数;因此,您对字段不感兴趣,而对构造函数的参数感兴趣。只是可以有多个参数,而且类不(必须)包含参数名。