Hodiya Eyal
Hodiya Eyal

Reputation: 63

The correct signature of a utility function to Array class: Comparable<E> or Comparable<? super E>?

I'm trying to write a utility function to the Array class.

The function should return the minimum element for all kinds of comparable types.

My question is which signature should the function has:

  1. public static <E> E min (Comparable**<E>**[] arr)
  2. public static <E> E min (Comparable**<? super E>**[] arr)

In Java arrays are variants and that means that if B extends A so A[] and B[] are related too, but ArrayList<A> and ArrayList<B> don't have the same connection.

This is the full code:

@SuppressWarnings("unchecked")
public static <E> E min (Comparable<E>[] arr){

    E min= null;

    if(arr.length > 0)          
        min = (E) arr[0];

    for (int i = 1; i < arr.length; i++) {

        if( (arr[i].compareTo((E) min)) < 0)
            min = (E) arr[i];
    }

    return min; 
}

Comment: I have two class A implements Comparable A and B extends A

When the signature is Comparable<? super E> then the call (from main) to:

A aArr[] = new A[] {new A(1), new B(2), new B(-1)};
B bb = min(aArr);

is a compiling error: cannoot convert from ...A to ... B, but when the signature of min() is Comparable<? super E> then the same call is just fine.

Thanks

Upvotes: 0

Views: 159

Answers (2)

Sweeper
Sweeper

Reputation: 272685

Neither of the signatures is a "correct" signature for this method, IMO. The correct signature is to accept a E[], where E extends Comparable<? super E>:

public static <E extends Comparable<? super E>> E min (E[] arr){

    E min= null;

    if(arr.length > 0)
        min = arr[0];

    for (int i = 1; i < arr.length; i++) {

        if( (arr[i].compareTo(min)) < 0)
            min = arr[i];
    }

    return min;
}

Your current implementation makes an incorrect assumption that "classes that implement Comparable<T> must be T or a subclass of T". This is not necessarily true. This is why you have to suppress those warnings.

Anyway, back to your actual problem. At the caller's side, you are doing B bb = min(aArr). In the Comparable<E> case, the error appears because the compiler could not infer what E should be. It can't be A because A can't be assigned to a variable of type B. It can't be B either, because you are giving it a A[], which is not a Comparable<B>[].

When you changed it to Comparable<? super E>, E can be B now. Since A[] is compatible with Comparable<? super B>.

But this only works in a really "hacky" way, because you are technically casting every element in the array to a B, since E is B, and because of how Java generics are erased, this doesn't fail.

The root of your problem really lies in the line B bb = min(aArr). You are making a possibly wrong assumption that the minimum of aArr is of type B. Why are you so sure? It could be an A as well, right? With my solution above, you need to cast the result to B if you are so sure that it is B:

B bb = (B)min(aArr);

Upvotes: 0

Steyrix
Steyrix

Reputation: 3236

The second signature works because A is superclass of class B, whereas it suits wildcard condition ? super B.

If you want your function to return minimum value of any comparable typed array, you just need.

public static <E extends Comparable<E>> min (E[] arr)

This will allow you to use the method for operating all kind of arrays, values of which have comparable type. In your case, you want A and B to be related. So you should also allow your method to take parameter of class E and it's superclasses. However, it is only needed, when you try to cast A to B, as in your example. That makes you want your method to take superclasses of E (as A is superclass of B) and still return value of type E.

public static <E extends Comparable<? super E>> min (E[] arr)

Upvotes: 1

Related Questions