Referencia dinámica a tipos de clase en Java

Necesitaba aplicar una serie de operaciones en distintos tipos de datos que comparten ciertas características. Hice lo lógico: creé una clase abstracta que implementa las funciones básicas de las características en común e hice que las demás clases fueran subclases de ella.

public abstract class BaseClass {

   // Propiedades y métodos comunes implementados aquí

}
public class SubClass1 extends BaseClass  {

   // Propiedades y métodos exclusivos
}
public class SubClass2 extends BaseClass {

    // Propiedades y métodos exclusivos
}

En los métodos que aplican las operaciones en cuestión declaré como parámetro la super clase (la abstracta) para que pudiera recibir cualquier subclase. El código quedó más o menos así.

public PriorityQueue<BaseClass> operation1 (List<? extends BaseClass> list) {
    // Hacer algo con los elementos en la lista
}

Hasta aquí todo bien.

El problema surgió cuando dentro de una de las operaciones tenía que modificar varias propiedades comunes de los objetos incluidos en la lista. Debido a la forma en la que Java pasa los parámetros (por valor,  aunque los objetos siempre pasan por referencia. Ver aquí), modificar en el método una de las propiedades del objeto no serviría de nada. Lo primero que se me vino a la mente fue crear clones de los objetos de la lista, pero muchos dicen que hay que evitarlos en la medida de lo posible, en especial cuando se tiene una estructura de clases compleja y algunos objetos no implementan la interface Clonable.

Ya he trabajado con clones antes y no hubo mucho problema. Sin embargo, quería ver si podía aplicar la solución de crear copy constructors para cada clase en la estructura y llamarlos dinámicamente. El problema es que necesitaba saber en tiempo real a qué clase pertenecían los objetos en la lista. Cierto: puedo hacer una serie de if con instanceof para probar cada una de las clases, pero se me hacía que había una mejor solución. Un simple:

BaseClass copy = new BaseClass(originalobject)

está fuera de contexto porque la clase base es abstracta (no se puede instanciar directamente).

La solución es relativamente sencilla: después de crear el copy constructor y de llamar en él al copy constructor de la superclase, en las operaciones tomo la clase de los elementos de la lista y creo dinámicamente una instancia de su copy constructor, la cual puedo llamar para crear una nueva instancia de la clase que contendrá los elementos del objeto original más lo que haya necesitado modificar. Al final meto todo en una cola que se ordena solita (basada en el compare de la clase) y es lo que regreso.

Los métodos que se usan:

  • Object.getClass() : para obtener en tiempo real la clase del objeto en cuestión.
  • Class.getDeclaredConstructor(Class) : para obtener el constructor que reciba los parámetros indicados. Como en este caso se trata de copy constructors, el único parámetro es un objeto de la misma clase, determinado con el getClass().
  • Constructor.newInstance(Object): para obtener una nueva instancia de la clase. En este caso, el constructor debe recibir como parámetro un objeto de la clase BaseClass o alguna de sus subclases.

Algo más o menos así:

public abstract class BaseClass {

   protected BaseClass() {
      // Inicialización de propiedades en común
   }

   // Copy Constructor
   protected BaseClass (BaseClass bc) {
       // copiar los datos de bc a esta instancia
   }

}

public class SubClass1 extends BaseClass {

    public SubClass1 {
       super();
       // Inicialización de propiedades exclusivas
    }

    // Copy Constructor
    public SubClass1 (SubClass1 sc1) {
        super(sc1);
        // Copiar los datos exclusivos de sc1 a esta instancia
    }
}

public class SubClass2 extends BaseClass {

    public SubClass2 {
       super();
       // Inicialización de propiedades exclusivas
    }

    // Copy Constructor
    public SubClass2 (SubClass2 sc2) {
        super(sc2);
        // Copiar los datos exclusivos de sc2 a esta instancia
    }
}

Y las operaciones:

public PriorityQueue<BaseClass> operation1 (List<? extends Searchable> list) {

    Class<? extends BaseClass> objclass = null;
    Constructor<? extends BaseClass> theConstructor = null;
    BaseClass newElement = null;

    for (BaseClass bc : list) {
       // Hacer algo con los datos

      objclass = bc.getClass();
      theConstructor = objclass.getDeclaredConstructor(objclass);
      newElement = theConstructor.newInstance(bc);

       // Hacer las operaciones restantes, crear la cola y devolverla
    }
}

Una alternativa más para la referencia dinámica a las clases y sus constructores.