Understanding Collections.reverseOrder() method in Java - java

Consider one of the overloaded definitions of sort method from Array class:
public static <T> void sort(T[] a, Comparator<? super T> c)
A common way to sort array in reverse order is to pass Comparator returned by Collections.reverseOrder() as a second argument to this method.
Let's look at the implementation of Collections.reverseOrder() method from openjdk 7:
public static <T> Comparator<T> reverseOrder() {
return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}
ReverseComparator class:
private static class ReverseComparator
implements Comparator<Comparable<Object>>, Serializable {
private static final long serialVersionUID = 7207038068494060240L;
static final ReverseComparator REVERSE_ORDER = new ReverseComparator();
public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c2.compareTo(c1);
}
private Object readResolve() { return reverseOrder(); }
}
My question is: why Collections.reverseOrder() is made to be generic? And why simply ReverseComparator.REVERSE_ORDER can't be returned?
Of course, we can specify type explicitly calling Collections.<T>reverseOrder(). But what's the benefit against simple Collections.reverseOrder() in this case?
I found a rather useful discussion there:
How does Collections.reverseOrder() know what type parameter to use?
But it doesn't answer to my question.
Also It's interesting for me how sort method uses compare method from ReverseComparator class. As we can see compare takes arguments of Comparable<Object> type. And what if we sort array of objects implementing Comparable<T>, where T is for example Integer? We can't invoke compare with Comparable<Integer> cause Comparable<Integer> isn't casted to Comparable<Object>.

why Collections.reverseOrder() is made to be generic?
This function is generic so as to spare you from having to cast the result into your specific Comparable<T> type. (You might say that you don't care because you don't cast it anyway, in which case what this tells us is that you do not have enough warnings enabled.)
why we can't simply return ReverseComparator.REVERSE_ORDER?
One reason is because ReverseComparator.REVERSE_ORDER is package-private, so you cannot access it from outside that package. Which in turn begs the question "why is it package-private?" Well, mainly because this satisfies the purists who cringe when they see member variables being directly accessed even if they are final, but actually I would not blame them in this case, because accessors offer forward compatibility at the binary level, which might be completely unnecessary in application code, but it kind of becomes a necessity in a language runtime. And ReverseComparator is part of the java runtime.
But a more important reason is because Collections.reverseOrder() does the cast to (Comparator<T>) for you, so that you don't have to do it yourself. (Again, if you don't see a problem with this, that's because you do not have enough warnings enabled, which means you need to reconsider your practices.)
In short, if you tried to do the following:
Comparator<MyObject> myComparator = ReverseComparator.REVERSE_ORDER;
you would get an error, because this is an invalid assignment. So, you would have to change it to this:
Comparator<MyObject> myComparator =
(Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;
but then you would get a warning, because this is an unchecked cast. So you would end up having to do this:
#SuppressWarnings( "unchecked" )
Comparator<MyObject> myComparator =
(Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;
which is ugly. So, Collections.reverseOrder() saves you from that, allowing you to do this:
Comparator<MyObject> myComparator = Collections.reverseOrder();
As we can see compare takes arguments of Comparable type. And what if we sort array of objects implementing Comparable, where T is for example Integer? We can't invoke compare with Comparable cause Comparable isn't casted to Comparable.
Okay, I see what your real question is. Welcome to the wonderful world of java generics and type erasure. I will try to explain, but be sure to also look up the term "type erasure" in order to fully understand the concept.
In java, generics were introduced to the language as an afterthought. For this reason, they had to be implemented in such a way that generics-aware code would be backwards compatible with old code which was not generics-aware. The solution was a trick called type erasure, which basically means that generic information is completely stripped after compilation. This means that at the bytecode level, Comparator<String> and Comparator<Integer> and Comparator are one and the same thing. No difference whatsoever. This is what enables the java runtime to implement a single class which acts as a reverse comparator of any object. It is not really a Comparator<Object>, it is a Comparator<ANYTHING>, because all it does is reverse the direction of the comparison, so it does not really care about the nature of the objects that are being compared.
So, in java, if you really know what you are doing, you are free to cast an instance of a generic class to an instance of the same class, but with a different generic parameter. In this case, the creators of the java runtime are casting Comparator<Object> to Comparator<T>, which may in fact be later assigned to Comparator<Integer>, and that's fine.
This cast is tricky though, because the compiler has to trust that you really know what you are doing, so by default, the compiler issues an "unchecked assignment" warning on such casts, and then in turn we indicate that we swear we know what we are doing with a #SuppressWarnings( "unchecked" ) annotation.
Collections.reverseOrder() spares you from having to be concerned with all that.

This is all about type erasure. Remember, at runtime there is no such thing as a Comparable<Object>, there is only such a thing as a Comparable. Therefore the compare method of REVERSE_COMPARATOR works on two String instances, for example. It doesn't cause a ClassCastException at runtime because String implements Comparable<String>, and at runtime that's just Comparable.
However, the method reverseComparator has to be generic, because otherwise the user would have to cast the returned object to the appropriate type before it could be used. For example, consider this code where the comparator has the same type as the declared type of REVERSE_COMPARATOR.
Comparator<Comparable<Object>> comparator = Collections.reverseOrder();
String[] arr = {"A", "B", "C"};
Arrays.sort(arr, comparator); // Doesn't compile.
The reason this doesn't compile is because arr is a String array, and so Arrays.sort expects a Comparator<? super String>, but Comparable<Object> is not a supertype of Comparable<String> (Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?).
You can make it compile by using casts:
Comparator<Comparable<Object>> comparator = Collections.reverseOrder();
String[] arr = {"A", "B", "C"};
Arrays.sort(arr, (Comparator<String>) (Object) comparator);
System.out.println(Arrays.toString(arr)); // prints [C, B, A]
This generates a warning, but as you will see it you try it, it works. By using a generic method, the ugliness of the cast and the warning is kept out of the code using the method.
The fact that the same object (REVERSE_COMPARATOR) can be treated as being a Comparator<String> or a Comparator<Integer>, or a Comparator<X> where X is any type implementing Comparable is one of the many benefits of type erasure. This reuse of objects is not possible in C# because in C# instances of a generic class know the type.
There are many examples of this kind of reuse. For example, all of these generic methods always return the same instance, no matter what generic type you supply.
Collections.emptySet()
Optional.empty()
Comparator.naturalOrder()
Collections.emptyListIterator()

Related

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.
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);
}
Just use a raw type. This compiles
public void sortAsString(Comparator<String> comp) {
List<Object> list = null;
Comparator foo = comp;
Collections.sort(list, foo);
}
Assuming that you are absolutely sure that the ArrayList<Object> contains only Strings, 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.

generics benefit when we can use parent object

I have learned that in compile time generics we use changes to most specific type. so what is the benefit of generics?
for example in this code:
int a=1;
double b = 2.1;
printArray(a,b);
public static <E> void printArray(E first , E second);
in compile time E changes to Number (because the specific parent of int and double is Number)
so what is the benefit of generics when we can write Number instead of E in here?
The purpose of generics is not related to the fact that the generic parameter will be inferred to be the most specific type. The purpose of generic methods is to allow you to write a method that can work with any type and at the same time, maintain type safety.
Your printArray method can not only take Number as arguments, but also String, SomeClass, Object and all sorts of reference types.
At this point you might ask "can't we just write Object as the parameter types?" Sure with the type being Object, the parameter will not only accept all reference types, but the primitives will also be accepted and boxed! So in this specific case, you don't necessarily have to use a generic method.
However, there are times when generic methods shine.
One of these occasions is when the methods work with generic types. For example, this is Optional.ofNullable:
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
It creates a Nullable<T> depending on whether the parameter is null. If this were not generic, it would have to return Optional<Object>. Every time you want to call get or orElse, you need to cast the result to the type you want. If you made a mistake in one of the casts, a ClassCastException might be thrown at runtime. But with generics, the compiler checks the types for you!
Here's another example:
public static <T> T first(List<T> list) { return list.get(0); }
Without generics, you would have to use the raw type List and return an Object. This again creates the problems of casting and reduced type safety.

why it is possible to new an array of an interface

Why the following is possible in Java ?
Integer[] ints = (Integer[])new Comparable[10];
But it gets ClassCastException at runtime. What is the usecase to new an array of an interface.
To answer the specific question:
Comparable toComplare[] = new Comparable[10];
Why not create an array that will allow you to store any object that implements the Comparable interface?!
The point is: the interface denotes a "common functionality" - and it could be helpful to only look at objects from that "view".
Of course, the objects stored in that array are always of some "real" class - but all these objects will implement the functionality that the Comparable interface provides.
So you could do things like:
toCompare[0] = new Integer(5);
toCompare[1] = new BigDecimal("3.2");
...
I am not saying that this is something that you would use frequently, but as said - it allows you to "collect" objects under a certain, specific "view" of their capabilities. It is also worth pointing out: having such an array does not mean that you would be able to do:
toCompare[0].compareTo(toCompare[1]);
successfully!
Beyond that: a cast always implies somehow that you, the programmer know something the compiler doesn't know. So the compiler steps back and lets you do that - assuming you know what you are doing. But as the code you are showing in the question is obviously not correct, reality comes back biting you at runtime. And yes, it would be possible to decide at compile that the given code is incorrect.
Regard this case: You have an interface and two (or more) classes that implement that interface:
interface MyInterface
{
public void someMethod();
}
class MyClass1 implements MyInterface
{
public void someMethod() { System.out.println("foo");}
}
class MyClass2 implements MyInterface
{
public void someMethod() { System.out.println("bar");}
}
And you call it like this:
public static void main(String[] args)
{
MyInterface[] array = new MyInterface[2];
array[0] = new MyClass1();
array[1] = new MyClass2();
array[0].someMethod();
array[1].someMethod();
}
An array of an interface gives you the method of holding different implementations of that interface in an array
The compiler looks at the type of the right side, and sees that it is an array of Comparable. In general, it could be an Integer[] (because that is assignable to Comparable[]).
We know that it will not be an Integer[], because that right-hand expression is a constructor call. But the compiler does not look that far. It uses the same logic as if that expression was a method call with a declared type of Comparable[]. It does not look inside to figure out the actual type.
So the compiler will accept your typecast, because it might succeed. It will only reject casts that cannot work out at all (according to declared types), such as casting Integer to String.
Note that it is probably a design flaw to allow this co-variance in arrays. You can cast Integer[] to Comparable[], but this has problems and for this reasons you cannot cast List<Integer> to List<Comparable>.
Having Integer implement Comparable, doesn't mean Integer[] implements Comparable[], so you can't convert arrays of different type. You can, however, put Integer in element of Comparable[] array.
Reason for ClassCastException is due to Heap Pollution.
Find more details here http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ050
Use case of new to an interface array / just interface is to fill it with any class object which implements that interface (or) give an anonymous inner class definition.
It is because you are performing a Narrowing Reference Conversion
The class Integer implements the Comparable interface:
public final class Integer extends Number implements Comparable<Integer>
see: 5.1.6. Narrowing Reference Conversion
From any array type SC[] to any array type TC[], provided that SC and TC are reference types and there is a narrowing reference conversion from SC to TC.
Such conversions require a test at run time to find out whether the actual reference value is a legitimate value of the new type. If not, then a ClassCastException is thrown.

casting a Collection of derived Java interfaces (generics) [duplicate]

This question already has answers here:
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
(19 answers)
Closed 8 years ago.
Making a API for internal use, I need some Java abstraction and I don't know how to do it.
There is an Interface A with derived interfaces.
Let's say one of the extended interfaces of A is called B.
And let's say there is an class C implementing B.
I have a Factory/Pool class F from which I want to get a list of instances (or set or similar collection).
What I want is basically this:
List<B> instances = F.getAllSuitableInstances(parameters);
Get me all the instances specified by my parameters as a collection of B.
First attempt was with this function signature
public List<A> getAllSuitableInstances(parameters);
but when I try to cast the resulting List to a List for use, I
get told "incompatible types" - even though B is derived from A.
So I tried what the java libraries do:
public <T> List<T> getAllSuitableInstances(parameters, T);
and try calling that with B as second parameter. But that doesn't work
either, since T has to be supplied as an instance for some reason, and
interfaces cannot have instances.
I know I could use <?> with casting but I'd like to make this as typesafe as possible.
So, what is the (best) way to get this done?
even though B is derived from A.
You have to be careful here -- Java's generics are invariant, meaning that Something<Superclass> is not a superclass of Something<Subclass> (e.g. List<Number> is not a superclass of List<Integer>). There are plenty of questions on SO about this, so I won't repeat information that other people can explain much more effectively, like here and here.
As for your second signature, it seems to me that the T in (parameters, T) is unnecessary.
public <T> List<T> getAllSuitableInstance(parameters);
should work, unless I'm misunderstanding your requirements. The disadvantage of this is that T could be anything, so you're somewhat limited in the methods you can invoke on objects of type T, but if you aren't doing that then no concern is warranted.
If the methods in your interfaces are necessary to get your method working (doesn't seem like it, but just in case), you can always add bounds (e.g. public <T extends A> List<T>...) to your generic type, so the compiler knows that all instances of T inside your method are type A or subtypes of A.
If you need to use the type of T in your method (say for instanceof checks), one option is passing in a Class object and using its methods to perform the runtime equivalent of instanceof checks and casting (among other options):
public <T> List<T> getAllSuitableInstance(parameters, Class<? extends T> clazz); // Might be able to use just Class<T> too
Hopefully that helps. Let me know if I got something wrong about what you needed.
This is a common misunderstanding when it comes to programming with generics, but it is an important concept to learn
when we have two concrete types A and B (for example, Number and Integer), MyClass<A> has no relationship to MyClass<B>, regardless of whether or not A and B are related.
The common parent of MyClass<A> and MyClass<B> is Object.
No need to even pass the class as you mentioned in second option
this should work for you
public <T> List<T> getAllSuitableInstances(parameters) {
List<T> list = new ArrayList<>();
//content add to list
return list;
}

Why is List<Number> not a sub-type of List<Object>?

public void wahey(List<Object> list) {}
wahey(new LinkedList<Number>());
The call to the method will not type-check. I can't even cast the parameter as follows:
wahey((List<Object>) new LinkedList<Number>());
From my research, I have gathered that the reason for not allowing this is type-safety. If we were allowed to do the above, then we could have the following:
List<Double> ld;
wahey(ld);
Inside the method wahey, we could add some Strings to the input list (as the parameter maintains a List<Object> reference). Now, after the method call, ld refers to a list with a type List<Double>, but the actual list contains some String objects!
This seems different to the normal way Java works without generics. For instance:
Object o;
Double d;
String s;
o = s;
d = (Double) o;
What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile.
This leads me to believe this is purely a design decision with regards to the type restrictions on generics. I was hoping to get some comments on this decision?
What you are doing in the "without generics" example is a cast, which makes it clear that you are doing something type-unsafe. The equivalent with generics would be:
Object o;
List<Double> d;
String s;
o = s;
d.add((Double) o);
Which behaves the same way (compiles, but fails at runtime). The reason for not allowing the behavior you're asking about is because it would allow implicit type-unsafe actions, which are much harder to notice in code. For example:
public void Foo(List<Object> list, Object obj) {
list.add(obj);
}
This looks perfectly fine and type-safe until you call it like this:
List<Double> list_d;
String s;
Foo(list_d, s);
Which also looks type-safe, because you as the caller don't necessarily know what Foo is going to do with its parameters.
So in that case you have two seemingly type-safe bits of code, which together end up being type-unsafe. That's bad, because it's hidden and therefore hard to avoid and harder to debug.
Consider if it was...
List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException
You would be able to add anything of the parent type to the list, which may not be what it was formerly declared as, which as the above example demonstrates, causes all sorts of problems. Thus, it is not allowed.
They aren't subtypes of each other due how generics work. What you want is to declare your function like this:
public void wahey(List<?> list) {}
Then it will accept a List of anything that extends Object. You can also do:
public void wahey(List<? extends Number> list) {}
This will let you take in Lists of something that's a subclass of Number.
I'd recommend you pick up a copy of "Java Generics and Collections" by Maurice Naftalin & Philip Wadler.
There are essentially two dimensions of abstraction here, the list abstraction and the abstraction of its contents. It's perfectly fine to vary along the list abstraction - to say, for instance, that it's a LinkedList or an ArrayList - but it's not fine to further restrict the contents, to say: This (list which holds objects) is a (linked list which holds only numbers). Because any reference that knows it as a (list which holds objects) understands, by the contract of its type, that it can hold any object.
This is quite different from what you have done in the non-generics example code, where you've said: treat this String as if it were a Double. You are instead trying to say: treat this (list which holds only numbers) as a (list which holds anything). And it doesn't, and the compiler can detect it, so it doesn't let you get away with it.
"What we are doing here is essentially
the same thing, except this will pass
compile-time checks and only fail at
run-time. The version with Lists won't
compile."
What you're observing makes perfect sense when you consider that the main purpose of Java generics is to get type incompatibilities to fail at compile time instead of run time.
From java.sun.com
Generics provides a way for you to
communicate the type of a collection
to the compiler, so that it can be
checked. Once the compiler knows the
element type of the collection, the
compiler can check that you have used
the collection consistently and can
insert the correct casts on values
being taken out of the collection.
In Java, List<S> is not a subtype of List<T> when S is a subtype of T. This rule provides type safety.
Let's say we allow a List<String> to be a subtype of List<Object>. Consider the following example:
public void foo(List<Object> objects) {
objects.add(new Integer(42));
}
List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);
So, forcing this provides type safety but it also has a drawback, it's less flexible. Consider the following example:
class MyCollection<T> {
public void addAll(Collection<T> otherCollection) {
...
}
}
Here you have a collection of T's, you want to add all items from another collection. You can't call this method with a Collection<S> for an S subtype of T. Ideally, this is ok because you are only adding elements into your collection, you are not modifying the parameter collection.
To fix this, Java provides what they call "wildcards". Wildcards are a way of providing covariance/contravariance. Now consider the following using wildcards:
class MyCollection<T> {
// Now we allow all types S that are a subtype of T
public void addAll(Collection<? extends T> otherCollection) {
...
otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
}
}
Now, with wildcards we allow covariance in the type T and we block operations that are not type safe (for example adding an item into the collection). This way we get flexibility and type safety.

Categories