samedi 5 mars 2016

Java: Should I use generics or not?

I'm working on an open-source Java library that will allow one to compute certain quantities, such as Gini index, of an attribute that takes on a finite number of values. (Formally, it computes the Gini index of the discrete distribution associated with the attribute A, but this is not relevant here.)

For example, one will be able to do the following

String[] namesArray = {"primary_school", "high_school", "university"};
Calculator<String> calc =new Calculator<String>(namesArray);

for (Person p : peopleCollection) {
    calc.increment(p.getEducationLevel());
}

// e.g. the Gini index of the distribution
double currentStat = calc.getCurrentValue();

The idea is to allow users of the library to use their own type to refer to attribute values; in this case, I am using strings (e.g. "primary_school"). But I might want to use integers or even my own type AttributeValue.

I solve this by defining

public class Calculator<T> {
    /* ... */
}

However, using generics causes some problems in the implementation: for example, if I want to maintain a collection of pairs of type (T, double), I have to do nasty type casts:

public class Calculator<T>
        /* ... */
        private Queue<StreamElement<T>> slidingWindow;
        /* ... */
        class StreamElement<T> {
                private T label;
                private double value;

                StreamElement(T label, double value) {
                        this.label = label;
                        this.value = value;
                }

                public T getLabel() {
                        return label;
                }
                public double getValue() {
                        return value;
                }
        }
        /* ... */
            slidingWindow.add(new StreamElement<T>(label, value));
            if (slidingWindow.size() > windowSize) {
                    StreamElement lastElement = slidingWindow.remove();
                    // XXX: Nasty type cast
                    decrement((T)lastElement.getLabel(), lastElement.getValue());
            }
        /* ... */
}

Here is the warning produced by javac:

Calculator.java:163: warning: [unchecked] unchecked cast
            decrement((T)lastElement.getLabel(), lastElement.getValue());
                                             ^
  required: T
  found:    Object
  where T is a type-variable:
    T extends Object declared in class Calculator
1 warning

Questions:

  • What is a proper, clean way to do the type cast?
  • What would be an alternative to using generics here?
  • More concretely, would it be better to instead define a class Label which user could extend to MyLabel and then use MyLabel for attribute values?

Aucun commentaire:

Enregistrer un commentaire