Attila Szegedi
Attila Szegedi

Reputation: 4595

Interesting generics-related discrepancy between javac and Eclipse IDE compiler

I have an interesting discrepancy between javac and Eclipse IDE compiler, and can't figure out who's right. So, the code below compiles with javac, however Eclipse tells me that the static initializer's invocation of "exportAll" is wrong, 'cause:

The method exportAll(Iterable< X.Stat< ? extends Number>>) in the type X is not applicable for the arguments (Collection< capture#1-of ? extends X.Stat>)

Who's right? javac or Eclipse?

import java.util.Map;

public class X {
  interface Stat<T> {
  }

  public static void exportAll(Iterable<Stat<? extends Number>> vars) {
  }

  public static Map<Double, ? extends Stat> getPercentiles() {
    return null;
  }

static {
  exportAll(getPercentiles().values());
}

}

Upvotes: 4

Views: 235

Answers (3)

corsiKa
corsiKa

Reputation: 82559

Your Map<Double, ? extends Stat>.values() will have a type of Collection<? extends Stat>. This Stat is really Stat<?>. Your Iterable requires that the Stat not just be any old Stat<?> but rather Stat<? extends Number>. You would have to change your getPercentiles() to be Map<Double, ? extends Stat<? extends Number>>.

public static void exportAll(Iterable<Stat<? extends Number>> vars) {
}                                  //      ^ this must match
                                   //     your values type on the map

public Map<Double, ? extends Stat<? extends Number>> getPercentiles() {
    return null;
}

@emboss here's what I would do in those cases whenever I could:

class Main {

    static interface Something<E> {
        void doSomething();
    }

    static class ConcreteSomething<E> implements Something<E> {
        E data;
        ConcreteSomething(E data) {
            this.data = data;
        }
        public void doSomething() {
            System.out.println(data);
        }
    }


    public static void main(String[] args) {
        List<Something<Number>> list = new LinkedList<Something<Number>>();
        list.add(new ConcreteSomething<Number>(Math.PI)); // an autoboxed Double
        list.add(new ConcreteSomething<Number>(new Integer(5))); // an Integer

        for(Something<Number> s : list) s.doSomething();
    }
}

Upvotes: 1

irreputable
irreputable

Reputation: 45433

It doesn't compile in javac. (after adding static to getPercentiles)

Get your facts straight; don't waste other people's time.

Kids today, too much ADD.

Upvotes: -1

emboss
emboss

Reputation: 39620

You can't compile your example - you are calling a non-static method getPercentiles from the static initializer, so I'll assume that it is a static method, too.

In any case, your compiler would at least spit out an "unchecked" warning if you compile with -XLint:unchecked (Stat takes a type parameter!). I assume you would like the following:

public class X {
    interface Stat<T> {
}

public static void exportAll(Iterable<? extends Stat<? extends Number>> vars) {
}

public static Map<Double, ? extends Stat<Double>> getPercentiles() {
    return null;
}

static {
    exportAll(getPercentiles().values());
}

I assume that your percentiles are an arbitrary subclass of Stat<Double>, therefore I declared them as ? extends Stat<Double> in the Map. So the values() call will return a Collection<? extends Stat<Double>>.

Collection implements Iterable, therefore we are safe on that side.But Collection<? extends Stat<Double>> is not covered by Iterable<Stat<? extends Number>>, therefore we need to declare the argument as Iterable<? extends Stat<? extends Number>>.

The beauty (well, except for the syntax) of having exportAll take Iterable<? extends Stat<? extends Number>> is that your Map could contain all sorts of ? extends Stats<N> where N is a subclass of Number.

Upvotes: 1

Related Questions