ZXH
ZXH

Reputation: 55

Generic with Bounds vs. Interface with Generic Types

Consider the following code

/**
 * Generic method with bounds
 */
public static <T> int countGreaterThan(Comparable<T>[] anArray,
        T elem) {
    int count = 0;
    for (Comparable<T> e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}
/**
 * Alternative to above
 */
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

It appears that both are functionally the same. However they can appear in the same class apparently overloading each other. When I use the following code, it seems the second method gets invoked. Without the second overloading method, the first method get invoked. Can someone provide an in-depth explanation?

    Integer[] array = new Integer[10];
    for (int i = 0; i < array.length; ++i)
        array[i] = i;

    System.out.println("Count > 5 = " + countGreaterThan(array, 5));

Upvotes: 2

Views: 101

Answers (3)

Sean Owen
Sean Owen

Reputation: 66886

(They can appear in the same class since they don't have the same erasure. The first one has a first argument of type Comparable[] and the second of type Object[].)

Both methods could apply; in Java, the method to call is determined at compile time and is always the more-specific overloading based on the argument's reference type.

Carlo Pellegrini makes a good point that the first would be called for any old Comparable. Greater experts may correct me, but I am fairly sure this is due to boxing. The call in your example binds Integer[] to T[] first, and then this induces the boxing of int to T which is now reified as Integer. In the case of his example Y, it can only match the first one.

Upvotes: 1

Carlo Pellegrini
Carlo Pellegrini

Reputation: 5686

Well, it's not a complete erasure.

The first method

<T> int countGreaterThan(Comparable<T>[] anArray,T elem)

works on an array of Comparable<T>, but does not mandate that elem is also Comparable.

You could check it on:

static class Y {
   int val;
   public Y(int val){
      this.val=val;
   }
} 

static class W extends Y implements Comparable<Y>{
   public W(int val){
      super(val);
   }
   public int compareTo(Y o){
      return this.val-o.val;
   } 
}

W[] array = new W[10];
for (int i = 0; i < array.length; ++i)
    array[i] = new W(i);

System.out.println("Count > 5 = " + countGreaterThan(array, new Y(5)));       

The first method will be called.

Upvotes: 2

JB Nizet
JB Nizet

Reputation: 691973

That's because in Java, an Integer[] is also a Comparable[]. If you had the same method with a list instead of an array as arguments, it wouldn't work the same way, because a List<Integer> is not a List<Comparable>. So only the second version would accept a List<Integer> as argument.

Collections are more type-safe and work better with generics than arrays. You should prefer them over arrays in general.

Upvotes: 0

Related Questions