Generics and Reflection are some of the few powerful tools which Java provides.

Use case: In an incremental software development model, we happen to change and change. And some changes are not compatible to each other. To keep things backward compatible, we need to make adapters.

For Example:

  1. Current use case only deals with Integer
  2. After some days the codebase starts dealing in Float
  3. Round three deals in Double.

To keep things backward compatible, we need our POJO to be easily convertible.

When Use case 2 arrives, we can simply do:
Integer.convertToFloat(Object) and vice versa.
And when Use case 3 arrives, now this will be a two step process:
Float.convertToDouble(Integer.convertToFloat(Object)) and so on, definitely not an evolutionary development!

In software development, More the lines, more are chances of error! How to get away with it, Generics and Reflection.

This problem translates to chain of responsibility design pattern. My Base class looks like:

public class BaseAdapter {
private Class source;
private Class destination;
protected Object object;
private static Multimap<Class, Node> graph = null;

//Please do not change these, they are tightly bound to AdapterInterface function names
private String convert = "convert";
private String checkIfConvertible = "checkIfConvertible";

BaseAdapter () {
if (graph == null || graph.isEmpty()) {
  graph = HashMultimap.create();

public BaseAdapter init (Object object, Class source, Class destination) {
this.object = object;
this.source = source;
this.destination = destination;

return this;

private boolean isConvertible(Node step, Object object) {
if (step.getKlass() == null) {
  return false;

try {
  Method method = step.getKlass().getMethod(checkIfConvertible, Object.class);
  if (!(boolean)method.invoke(getInstances(step.getKlass()), object)) {
    return false;
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {

return true;

protected final void definePath(Class source, Class destination, Class className) {
Node node = new Node();


graph.put(source, node);

private Set<Node> nextStepsInOrder (Class source, Class destination){
return getInstance(GraphHelper.class).findShortestPath(source, destination, graph);

public void preCompute() {
//ToImplement Computes all paths and stores at the time of initialization;


protected Optional execute () {
Set<Node> stepsInOrder = nextStepsInOrder(source, destination);

Optional object = Optional.of(this.object);

for (Node step : stepsInOrder) {
  try {
    if (!isConvertible(step, object.get())) {
      return Optional.empty();
    Method methodConvert = step.getKlass().getMethod(convert, step.getSource());
    object = (Optional) methodConvert.invoke(getInstance(step.getKlass()), object.get());
  } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {

return object;

getInstance is a static function which gives the singleton instance of class.

Not to Enforce methods, we need some interface with generics.

public interface AdapterInterface <T1, T2> {

//Please do not change these function signatures, they are tightly coupled to BaseAdapter
Optional<T2> convert(T1 object);
boolean checkIfConvertible(Object object); //Invoked via reflection, ignore the warning of not used

And we need a driver class to run it, The driver class will look like:

public class AdapterDriver{

  BaseAdapter baseAdapter;

public AdapterDriver(){
//All these are required to run the converter as these initialize the class and help in making graph.
baseAdapter = getInstance(BaseAdapter.class);

public Optional process(Object object, Class source, Class destination) {
return baseAdapter.init(object, source, destination).execute();

Now the adapters will look like:

public class V2toV3Adapter extends BaseAdapter implements AdapterInterface<ObjectV2, ObjectV3>{
public Optional<ObjectV3> convert(ObjectV2 object) {
//Actual conversion logic!
return Optional.of(new ObjectV3());

public boolean checkIfConvertible(Object object) {
return true; //Checks for compatibility

V2toV3Adapter() {
super.definePath(ObjectV2.class, ObjectV3.class, V2toV3Adapter.class); //This initializes the graph to compute shortest path

The code now has two problems, how to find the shortest conversion path in a directed graph and how to invoke.

For finding shortest path, we can use the mix of Dijkshtra's algorithm plus topological sort.

public class GraphHelper {

  public Set<Node> findShortestPath(Class source, Class destination, Multimap<Class, Node> graph) {
Set<Class> traversed = Sets.newHashSet();
List<Class> sources = Lists.newArrayList();
List<Class> keyset = Lists.newArrayList();
List<Node> destArr = Lists.newArrayList();
List<Class> destAssociatedArr = Lists.newArrayList();


boolean isFinished = false;


//This part will convert cyclic graph to DAG
while (!sources.isEmpty() && !isFinished) {
  List<Class> nextNodes = Lists.newArrayList();
  for (Class sourceTemp : sources) {
    if (!traversed.contains(sourceTemp)) {
      Collection<Node> destinationsTemp = graph.get(sourceTemp);
      for (Node destinationTemp : destinationsTemp) {
        if (destinationTemp.getDestination().equals(destination)) {
          modifyIfRequired(sourceTemp, destinationTemp, destArr, destAssociatedArr);
          isFinished = true;
        } else {
          if (!traversed.contains(sourceTemp)) {
            modifyIfRequired(sourceTemp, destinationTemp, destArr, destAssociatedArr);
      if (isFinished) {

findRoute(keyset, destArr, destAssociatedArr);

//LinkedHashSet is important for ordering, please do not change the DS
//Not using queues to avoid duplicates, if any
Set<Node> retval = Sets.newLinkedHashSet();

return retval;

private void modifyIfRequired(Class source, Node destNode, List<Node> dest, List<Class> destAssociatedArr) {

private void findRoute(List<Class> keyset, List<Node> dest, List<Class> destAssociatedArr) {
boolean visited[] = new boolean[keyset.size()];
Arrays.fill(visited, false);

//Following code slashes off unnecessary vertices from traversing.
for (int i = 0; i < destAssociatedArr.size(); i++) {
  Class pivot = destAssociatedArr.get(i);
  for (int j = i; j < dest.size(); j++) {
    if (dest.get(j).getDestination().equals(pivot)) {

Now to invoke, all we need to do is:
getInstance(AdapterDriver.class).process(objectV2, ObjectV(i).class, ObjectV(j).class)

One line to rule them all!

My node looks like:
public class Node { private Class destination; private Class source; private Class klass; }

PS: Google collect and lombook libraries are extensively used in the code, you need to rework some bits and pieces of it to make it work!

Happy Coding, and optimisations are most welcome :)