Reputation: 5598
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.
Upvotes: 1
Views: 122
Reputation: 132460
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);
}
Upvotes: 4
Reputation: 272750
Assuming that you are absolutely sure that the ArrayList<Object>
contains only String
s, and you are prepared for it to crash if that is not the case...
You can create a Comparator<Object>
from the Comparator<String>
, that will crash if a non-String
gets passed to it:
Collections.sort(list, (x, y) -> comp.compare((String)x, (String)y));
I would suggest checking if all the objects are strings before that, and throw an exception with a meaningful message/do nothing if that is not the case.
Upvotes: 0
Reputation: 11464
Just use a raw type. This compiles
public void sortAsString(Comparator<String> comp) {
List<Object> list = null;
Comparator foo = comp;
Collections.sort(list, foo);
}
Upvotes: 0