Inicio > Informática > Generics en Java con Numbers (II)

Generics en Java con Numbers (II)


En un post anterior ya comentaba la solución que había adoptado para poder trabajar con genéricos utilizando números en Java. Básicamente consistía en utilizar doubles como representación interna y tener una clase encargada de realizar los cast, ya que no se puede hacer un cast de Double a Integer, por ejemplo.

Esta solución no terminaba de gustarme, ya que la sobrecarga tanto de espacio en memoria como de tiempo de cómputo es muy grande si el tipo de número que queremos representar no es double. El mayor desafío con el que me encuentro es que la clase Number no implementa métodos para realizar operaciones básicas (suma, resta, comparación, etc.). En teoría no hace falta gracias al auto-boxing/unboxing, pero cuando el tipo es genérico, aunque explícitamente se especifique que es un Number (<T extends Number>) esto no funciona.

La nueva solución consiste en tener una clase que realiza las operaciones básicas, incluyendo un constructor desde entero y doble.

public interface NumberOperator<T extends Number> {
    // Constructors
    /**
     * Builds the Number from an integer value
     * @param value integer value
     * @return The Number class
     */
    public T build(int    value);
    /**
     * Builds the Number from a double value
     * @param value double value
     * @return The Number class
     */
    public T build(double value);
    /**
     * Return the max possible value of T
     * @return the max possible value of type
     */
    public T maxValue();
    /**
     * Return the min possible value of T
     * @return the min possible value of type
     */
    public T minValue();
    // Binary operators
    /**
     * Add second to first
     * @param first
     * @param second
     * @return first + second
     */
    public T add     (T first, T second);
    /**
     * Subtract second to first
     * @param first
     * @param second
     * @return first - second
     */
    public T subtract(T first, T second);
    /**
     * Divide first by second
     * @param first
     * @param second
     * @return first / second
     */
    public T divide  (T first, T second);
    /**
     * Multiply first by second
     * @param first
     * @param second
     * @return first * second
     */
    public T multiply(T first, T second);
    // Comparison
    /**
     * Return true if first is greater than second. False otherwise
     * @param first
     * @param second
     * @return true if first is greater than second. False otherwise
     */
    public boolean greater(T first, T second);
    /**
     * Return true if first is lesser than second. False otherwise
     * @param first
     * @param second
     * @return true if first is lesser than second. False otherwise
     */
    public boolean lesser (T first, T second);
    /**
     * Return true if first is equal than second. False otherwise
     * @param first
     * @param second
     * @return Return true if first is equal than second. False otherwise
     */
    public boolean equal  (T first, T second);
}

Ahora para cada tipo que quiera (Double, Integer, Float…) implemento esta interfaz:


public class DoubleOperator implements NumberOperator<Double> {

    public Double build(int value) {
        return (double) value;
    }

    public Double build(double value) {
        return value;
    }

    public Double add(Double first, Double second) {
        return first + second;
    }
    ...
}

Para crear los NumberOperator utilizamos la clase del tipo (Integer, Double, etc), igual que en el post anterior, y las operaciones no tenemos más que hacerlas con la clase NumberOperator.


public class DistancesImpl<T extends Number> {
   T[][] distances;
   NumberOperator operator;
   public DistancesImpl(Class TClass) {
       if(Double.class.equals(TClass)) {
            operator = new DoubleOperator();
      } else if (Integer.class.equals(TClass)){
            operator = new IntegerOperator();
      }
      ...
   }

   public T get(int i, int j) {
       return distances[i][j];
   }

   /**
    * Return the sum of distances from node n to the rest of nodes
    */
   public T sum(int n, List<Integer> nodes) {
       // Init type to 0
       T sum = operator.build(0);
       for(int node: nodes) {
              sum = operator.sum(sum, distances[n][node]);
       }
   }
}

Definitivamente, esta solución me gusta más que la anterior, pero no deja de ser un apaño que hay que hacer para lidiar con los problemas de usar los genéricos + tipos básicos de Java.

Categorías:Informática Etiquetas: ,
  1. junio 26, 2010 a las 2:57 am

    Buena opción. Igual la clase Number de Java es una porquería, no podés hacer casi nada. La otra vez quise hacer un validador de números polimórfico y tuve que hacer algo parecido a esto… Smalltalk tiene una jerarquía mucho mejor que esta payasada para representar números. Y los números son polimórficos en serio.

    • junio 26, 2010 a las 9:40 am

      Yo estuve en una conferencia de Alan Kay, creador de SmallTalk. La verdad es que hablaba maravillas de su lenguaje, y mostró aplicaciones muy buenas; pero no es una alternativa realista para programar en una empresa, al menos en España, donde no puedes salir de Java o .Net

  2. JavaBoy
    febrero 11, 2012 a las 4:29 am

    Algo tarde pero… bueno la solucion al problema que plantean (no se porque llorar tanto si al final siempre hay soluciones =))

    public class DistancesImpl {

    public Class getClassType {
    ParameterizedType parameterizedType =
    (ParameterizedType) getClass().getGenericSuperClass();
    return (Class) parameterizedtype.getActualTypeArguments()[0];
    }

    }

    Para tu caso funciona 100% pero si DistancesImpl fuera abstracto ya no funcionaria….

    • febrero 11, 2012 a las 11:57 am

      Efectivamente, un poco tarde. Y de nuevo, efectivamente, sería una mejora para no tener que pasarle al constructor la clase T, pero no era ese mi mayor problema.

      El problema era no poder utilizar directamente operadores (+, *, etc.) con los elementos del vector distancias. Ese problema estaba resuelto con la solución dada, aunque no deja de ser un apaño que en c++ no es necesario hacer.

      No soy fan acérrimo de ningún lenguaje; cada uno tiene “lo suyo”. Pero, sin duda, los generics en Java son una chapucilla.

  3. JavaBoy
    febrero 11, 2012 a las 4:30 am

    No se porque no salio el … en fin ya saben que va alli =)

  4. JavaBoy
    febrero 11, 2012 a las 4:31 am

    No sale el (menor) T extends Number (mayor)…. -.-

  1. junio 25, 2010 a las 10:54 am
  2. junio 25, 2010 a las 10:54 am

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s