6

Related to that question.

I know about wildcard capturing. For instance, the following could be used for reversing a list:

public static void reverse(List<?> list) { rev(list); } //capturing the wildcard
private static <T> void rev(List<T> list) {
    List<T> tmp = new ArrayList<T>(list);
    for (int i = 0; i < list.size(); i++) {
        list.set(i, tmp.get(list.size()-i-1));
    }
}

Now I'm trying to write the same thing for that kind of situation:

private int compare (Comparable<?> upper, Comparable<?> lower){
    return comp(upper, lower);  //The method comp(Comparable<T>, Comparable<T>) is not applicable for the arguments (Comparable<capture#5-of ?>, Comparable<capture#6-of ?>)
}

private <T> int comp(Comparable<T> upper, Comparable<T> lower){
    return upper.compareTo((T) lower);
}

I expected that it was compiled fine as well. Is it possible to capture wildacrds for methods with two or more parameters that way?

Community
  • 1
  • 1
user3663882
  • 6,957
  • 10
  • 51
  • 92

2 Answers2

4

Because, as I said in my answer to your other question, the compiler cannot know that the two ? stand for the same type.

The two ? each stand for some unknown type. The compare method needs two Comparable objects for the same type T. If you call compare from the comp method, the compiler cannot be sure that the two ? stand for the same type.

Jesper
  • 202,709
  • 46
  • 318
  • 350
  • But what if we replace the generics with raws? What troubles we will probably get in? – user3663882 Jun 08 '15 at 07:33
  • If you use raw types, compiling will pass, however, you'll get a warning saying that at Runtime, a ClassCastException is likely to occur. And Generics help avoiding such errors. – Konstantin Yovkov Jun 08 '15 at 07:35
  • When you use raw types, the compiler goes into backward compatibility mode with less type checking. But you shouldn't use raw types; see: [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/2770692) – Jesper Jun 08 '15 at 07:35
2

In this method

private <T> int comp(Comparable<T> upper, Comparable<T> lower){
    return upper.compareTo((T) lower);
}

both of the parameters share the same type-parameter.

Meanwhile, this is not true for the other method:

private int compare (Comparable<?> upper, Comparable<?> lower){
    return comp(upper, lower); 
}

Here, the compiler has no evidence that the type-parameters for upper and lower are the same and that's why refuses to give green light to compilation.

If you want both of the methods to share the same type-parameter(s), you can make the type-parameter class-scoped. For example:

public class YourClass<T> {
     private int comp(Comparable<T> upper, Comparable<T> lower){
         return upper.compareTo((T) lower);
     }

     private int compare (Comparable<T> upper, Comparable<T> lower){
         return comp(upper, lower); 
     }
}

Another option (if you don't like the first one) would be to introduce one and the same upper bound for the type-parameters for comp() and compare(). For example:

private <T extends SomeSuperClass> int comp(Comparable<T> upper, Comparable<T> lower){
    return upper.compareTo((T) lower);
}

private <T extends SomeSuperClass> int compare (Comparable<T> upper, Comparable<T> lower){
    return comp(upper, lower); 
}

Furthermore, if you want to avoid the casting in the comp() method, you can do:

public class YourClass<T extends SomeSuperClass & Comparable<T>> {
     private int comp(T upper, T lower){
         return upper.compareTo(lower);
     }

     private int compare (T upper, T lower){
         return comp(upper, lower); 
     }
}
Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147