Reputation: 2219
I am trying to create a generic row mapper for JDBC.
My initial attempt looks like:
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;
}
}
}
But in the constructor, the getComponentType() is returning null, so clazz = null;
and I am not being able to make any progress.
What am I doing wrong?
Upvotes: 0
Views: 1117
Reputation: 102903
What I am doing wrong?
A mistake many newbie programmers make (and some experts, too!).
You have failed to read documentation.
Had you done so, you may have noticed that the docs for getComponentType()
tell you that it returns the component type of an array.
Given, say, Class<?> c = String[].class
, I can call c.getComponentType()
and I get String.class
back. Or new String[10].getClass().getComponentType()
would get me the same thing.
You're clearly invoking .getComponentType()
on a class that is not representing an array type, thus, you get null
back.
Your code is rather confused. T
is a type variable, thus, taking a parameter of T instance
means you must provide an actual instance of it, i.e. if you have a RowMapper
that maps a row into a string, you'd have to provide a string, which doesn't make sense. Presumably what you actually want as arg is Class<T>
instead, in which case - voila, there is your class. No need to invoke .getClass()
or .getComponentType()
on it in the first place. If truly you intended for this method to require that you pass an actual instance first, .getClass()
would be the method you presumably want, except I don't think that's going to work: .getClass()
always gives you the actual type, i.e. calling .getClass()
on a variable of type List cannot ever return List.class
- it would return perhaps ArrayList.class
or some anonymous inner class that implements List.
.getDeclaredFields
() only returns the fields in the class definition itself, not any parent classes (whose fields are just as much part of any instance of the class as any other) - again, read docs to figure this out.
'print a stack trace, return null' is very silly exception handling. The right 'I do not (yet) care about it' exception handler is throw new RuntimeException("unhandled", e);
- shorter, better. There is no reason not to. Fix your IDE's templates if you must.
Many, many modern takes on java (notably including the record
feature) has an immutable class where you therefore cannot just 'set' a bunch of fields, and there won't be a no-argument constructor (which .getConstructor()
gets you, thus, .getConstructor()
would fail). Instead, the constructor itself takes all args; therefore, you're not interested in the fields, you're interested in the parameters of the constructor. Except there could be more than one, and classes do not (neccessarily) contain the names of parameters.
Upvotes: 2