2

I have following code

public class GenericTest {
    public <T extends Number> void someMethod(){
        Map<T, String> map = new HashMap<>();
        someLegacyMethod(map.keySet());  // COMPILER ERROR
    }

    public String someLegacyMethod(Set<Number> nums){
        //some logic
        return "";
    }
}

I was expecting that someLegacyMethod can be called as is as T is of type Number. But compiler gives me an error.

The method someLegacyMethod(Set<Number>) in the type GenericTest is not applicable for the arguments (Set<T>)

So either I have to modify the legacy method to take it typed argument, or cast the method parameter everytime I call the method. Why doesn't the above code work? And is there any other cleaner way to make this work?

sidgate
  • 14,650
  • 11
  • 68
  • 119
  • 1
    what is the error you are getting? – Shriram Feb 02 '15 at 09:10
  • question updated with error – sidgate Feb 02 '15 at 09:13
  • Is there any reason why you can't cast it? `someLegacyMethod((Set) map.keySet());` – vikingsteve Feb 02 '15 at 09:16
  • @vikingsteve because Set is not the same as Set. The first one could be a Set while the second can contain all kind of Numbers (Long, Double, Float, Integer, ...). You could get away by casting without typing the Set, but then you could end up with heap pollution. Only good solution is to copy the Set into a new Set – Guillaume Polet Feb 02 '15 at 09:28

4 Answers4

3

The problem is with the compatibility between the Set<T extends Number> (from the map) and Set<Number> (the someLegacyMethod parameter).

T extends Number may be replaced at Runtime with Integer, Long, Double, etc. and then the map.keySet() will return a Set<Integer>, Set<Long>, Set<Double> respectively.

Now the question that stands is

Is the Set<Double> (for instance) a subclass of Set<Number> ?

The answer is no.

From the name of your someLegacyMethod() I can assume you can't introduce a generic type for it. The only thing you can do is to copy the content of the keyset to a Set<Number>:

public <T extends Number> void someMethod() {
    Map<T, String> map = new HashMap<>();
    Set<Number> set = new HashSet<Number>(map.keySet());
    someLegacyMethod(set);
}
Community
  • 1
  • 1
Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147
0

map.keySet() returns the keyset with the type as T for your someLegacyMethod Change Set<Number> to Set<T>.

Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147
Shriram
  • 4,343
  • 8
  • 37
  • 64
  • If he changes the `Set` to `Set` the code won't compile unless he introduces a generic type `T extends Number` for `someLegacyMethod()`. Also, the name of `someLegacyMethod()` hints that this method is a legacy one and cannot be changed. – Konstantin Yovkov Feb 02 '15 at 09:35
0

You should create new wrapper method which checks if is instanceOf or throws illegalArgumentException.

     public String wrapperOfSomeLegacyMethod(Set<T extends Number> nums) throws IllegalArgumentException{
               //itervate over num. check if isntance of Number 
               //if yes, call someLegacyMethod
               //if no , throw illegalArgumentException

        }
Pranalee
  • 3,389
  • 3
  • 22
  • 36
0

Because it doesn't recognize T i would do it this way:

public class GenericTest <T extends Number> {
     public T void someMethod(){
        Map<T, String> map = new HashMap<>();
         someLegacyMethod(map.keySet());  // COMPILER ERROR
     }

     public String someLegacyMethod(Set<T> nums){
        //some logic
        return "";
     }
}

And then instance your class this way:

new GenericText<ExtendsNumber>();
MineConsulting SRL
  • 2,340
  • 2
  • 17
  • 32