Skip to content
Advertisement

How to create a type specific Comparator for a flexible type array?

I am stuck in the middle of ‘generic-type-qualifier-land’ and can’t find the right combination of type qualifiers. I have a class that represents a flexible array that can have a number of different types in it. The class holds an ArrayList<Object> as the container for the elements.

class MyArray {
    ArrayList<Object>  list;

    ...

    public void sortAsString(Comparator<String> comp) {
        Collections.sort(list, comp);
    }
}

There are type specific accessors, like getString(i) and getInt(i) etc.

I would like to be able to sort this array in different ways. Sometimes the array is full of strings, and I would like people to be able to pass in a Comparator<String> object. If I declare the class as above, the compiler rejects it because the list is a collection of Object while the Comparator is a Comparator<String>

But I can’t type-cast either. This does not work:

    public void sortAsString(Comparator<String> comp) {
        Coparator<Object> foo = (Comparator<Object>) comp;
        Collections.sort(list, foo);
    }

The Collections.sort function second parameter requires Comparator<? super Object>. Is there any way to declare the method and/or to type cast such that someone can pass a Comparator<String> and have it work on this ArrayList<Object>?

I also tried casting the list to ArrayList<String> but that is not allowed either. In the cases where this is called, the user is going to be fairly sure that the array has only strings in it, but of course we don’t know that at compile time. I am trying to tell it that even though it is not declared as an array of string, go ahead and treat it like one for sorting. I don’t care if it blows up when the member object turns out to not be a string. I just some syntactic sugar to tell the compiler to go ahead and let it call the sorting method.

Advertisement

Answer

You’re on the right track trying to cast the list to ArrayList<String> (or perhaps just List<String>). The compiler flags this as an error, though, because in general it’s definitely unsafe.

If it’s safe for the caller to make this call, you can use the “cast through raw” technique to avoid the compilation error. You’ll still get an unchecked warning, which you can then suppress. Also, it’s probably a good idea to check the contents of the list before making the unsafe cast. This allows you to verify that what you’re doing is in fact safe, or if it isn’t, you can communicate that to the caller in the appropriate way — possibly by throwing an exception. For example,

    public void sortAsString(Comparator<String> comp) {
        if (! list.stream().allMatch(o -> o instanceof String)) {
            throw new IllegalStateException("MyArray contains non-strings");
        }
        @SuppressWarnings("unchecked")
        ArrayList<String> temp = (ArrayList<String>)(ArrayList)list;
        temp.sort(comp);
    }
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement