0

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);

// p.getEducationLevel() returns `"primary_school"`, `"high_school"`, or `"university"`.
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

Update. If I do not do the type cast, I get

Calculator.java:163: error: no suitable method found for decrement(Object,double)
            decrement(lastElement.getLabel(), lastElement.getValue());
            ^
    method Calculator.decrement(T) is not applicable
      (actual and formal argument lists differ in length)
    method Calculator.decrement(T,double) is not applicable
      (actual argument Object cannot be converted to T by method invocation conversion)
  where T is a type-variable:
    T extends Object declared in class Calculator
1 error

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? This means that Calculator would no longer be a generic type; in the implementation we'd have class StreamElement { Label label; /* ... */ } et cetera.
blazs
  • 4,705
  • 24
  • 38
  • 1
    Possible duplicate of [What is a raw type and why shouldn't we use it?](http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – fabian Mar 05 '16 at 16:11
  • Here's a good tip for your generics: [Java PECS](http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs) – Rogue Mar 05 '16 at 16:13
  • Well, it basically comes down to the question I linked, since you don't specify the type parameter here: `StreamElement lastElement` – fabian Mar 05 '16 at 16:14
  • Dear fabian, I believe the question is not a duplicate. I am asking what a Java expert would (advise me to) do in this specific case. – blazs Mar 05 '16 at 16:14
  • Which type is `slidingWindow` of? – Moritz Petersen Mar 05 '16 at 16:26
  • It says in the code above: `private Queue> slidingWindow;` It's instantiated as `ArrayDeque>` – blazs Mar 05 '16 at 16:27
  • bummer ;-) you're right I'm blind – Moritz Petersen Mar 05 '16 at 16:28

1 Answers1

2

I think you just made some mistake.

This is the correct implementation:

        /* ... */
            slidingWindow.add(new StreamElement<T>(label, value));
            if (slidingWindow.size() > windowSize) {
                    // Don't forget the generic argument at StreamElement
                    StreamElement<T> lastElement = slidingWindow.remove();
                    decrement(lastElement.getLabel(), lastElement.getValue());
            }
        /* ... */
Moritz Petersen
  • 12,902
  • 3
  • 38
  • 45
  • Okay, I tried to simplify the question and I didn't mention that I am implementing a generic interface. So `decrement` is defined as part of an interface `Interface` which `Calculator` implements. See the error I get (see update to the answer). I don't know whether this makes a difference---I am inexperienced in Java. – blazs Mar 05 '16 at 16:33
  • 1
    Ah... this is embarrassing. I didn't notice I was missing the generic argument. – blazs Mar 05 '16 at 16:41
  • It's okay, I deserved it. :) It's ironic I wrote a whole post wihout noticing it. – blazs Mar 05 '16 at 16:43