Немного не верное утверждение, что генерики существуют только на момент компиляции, точнее не во всех случаях. Например при наследовании от класса с генериками или имплементации генерик интерфейса, у нас есть возможность в рантайме получить полную информацию о самих генериках. Тоесть наш класс конвертера будет иметь примерно такой вид:
public class Converter<A, B> {
final Class<? extends A> retClass;
protected Converter() {
retClass = findType(getClass());
if (retClass == null) {
throw new RuntimeException("Error while determine generic types");
}
}
public A convert(B from) {
A res = null;
try {
res = retClass.newInstance();
Map<String, Field> bFields = getDeclaredAndInheritedFields(from.getClass(), false);
Map<String, Field> aFields = getDeclaredAndInheritedFields(retClass, false);
for(Field field : bFields.values()) {
if (aFields.containsKey(field.getName())) {
Field aField = aFields.get(field.getName());
if (aField.getType().isAssignableFrom(field.getType())) {
field.setAccessible(true);
aField.setAccessible(true);
aField.set(res, field.get(from));
} else {
// типы не приводятся
}
}
}
} catch (InstantiationException | IllegalAccessException e) {
//Logger.getLogger(Transparent.class.getName()).log(Level.SEVERE, null, ex);
}
return res;
}
@SuppressWarnings("unchecked")
private Class<A> findType(Class<? extends Converter> clazz) {
ParameterizedType genericSuperclass = (ParameterizedType) clazz.getGenericSuperclass();
Type type = genericSuperclass.getActualTypeArguments()[0];
if (type instanceof Class<?>) {
return (Class<A>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class<?>) {
return (Class<A>) rawType;
}
}
return null;
}
public static Map<String, Field> getDeclaredAndInheritedFields(Class<?> type) {
Map<String, Field> allFields = new HashMap<>();
allFields.putAll(getValidFields(type.getDeclaredFields()));
Class parent = type.getSuperclass();
while (parent != null && parent != Object.class) {
allFields.putAll(getValidFields(parent.getDeclaredFields()));
parent = parent.getSuperclass();
}
return allFields;
}
public static Map<String, Field> getValidFields(Field[] fields) {
Map<String, Field> allFields = new HashMap<>();
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
allFields.put(field.getName(), field);
}
}
return allFields;
}
}
Для конвертации из объекта класса BC в объект класса AC можно воспользоваться вот таким способом:
public static class AC {
private String one;
private Date two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public Date getTwo() {
return two;
}
public void setTwo(Date two) {
this.two = two;
}
}
public static class BC {
private String one;
private Date two;
public BC(String one, Date two) {
this.one = one;
this.two = two;
}
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public Date getTwo() {
return two;
}
public void setTwo(Date two) {
this.two = two;
}
}
public static void main(String[] args) {
BC bObj = new BC("Test", new Date());
AC obj = new Converter<AC, BC>(){}.convert(bObj);
boolean same = obj.getOne().equals(bObj.getOne()) && obj.getTwo().equals(bObj.getTwo());
if (same) {
System.out.print("Object the same");
}
}
НО: на каждую такую запись
new Converter<AC, BC>(){} в коде будет создан новый анонимный класс, который унаследуется от
Converter<AC, BC> .
Я бы такой код не использовал, лучше сделать метод который будет принимать либо два объекта один пустой другой полный, либо передвать полную информацию о классах в метод.