Difference in Generic Signature in Java - java

We have made in a tutorial an example with the following signture (as part of an interface)
<T> List<Comparable<T>> sort(Collection<Comparable<T>> c, boolean ascending);
We have found it nearly impossible to implement that method without warnings:
public <T> List<Comparable<T>> sort(Collection<Comparable<T>> c, boolean ascending) {
List<T> list = new ArrayList<T>();
Collections.sort(list);
return list;
}
The error in the line Collections.sort(list) we get is:
Bound mismatch: The generic method sort(List<T>) of type Collections is not
applicable for the arguments (List<T>). The inferred type T is not a valid
substitute for the bounded parameter <T extends Comparable<? super T>>
However, it works for the following signature:
<T extends Comparable<T>> List<T> sort(Collection<T> c, boolean ascending);
With that signature, the code above (implementation of sort) just works as expected. I would like to know what is the reason for that.

A list of Comparable<T>s is a list of objects which are comparable with Ts. One could not say if these objects themselves are Ts. Thus it's impossible to compare two elements with each other.
Ts, which happen to be comparable with other Ts, can be compared with each other. And so sorting a list of such Ts is possible.

Collections.sort expects a List<T extends Comparable<? super T>>, i.e. the compiler needs to be able to tell that the element type T of the list it is sorting extends Comparable<E> where E is T, a superclass of T or an interface implemented by T. Your first signature with public <T> List<Comparable<T>> sort doesn't enforce this, so you can't Collections.sort a List<T>. You could make it work by saying
List<Comparable<T>> list = new ArrayList<Comparable<T>>();
to match the type that your sort method returns, but the problem with this is that it's rather inflexible - it can only sort a Collection<Comparable<Foo>>, not a Collection<Bar> where Bar implements Comparable<Foo>. The most flexible approach would be to stick with the new ArrayList<T>() but use a signature like
<T extends Comparable<? super T>> List<T> sort(Collection<T> c, boolean ascending);
which places the minimum restriction on T to make the sort valid - it would work with Bar extends Foo implements Comparable<Foo>, which your second signature doesn't allow (that would require Bar implements Comparable<Bar>).

Related

Trouble Understanding Comparable<T> cast

I just wrote a method that takes two arguments: 1. An array list of any type that extends Number, and 2. a number of the same type. This method should return an array list of all numbers less than the second argument.
My class is called Quiz3FinalQuestion<T extends Comparable>>
My method I wrote looks like this,
public static <T extends Number> ArrayList<T> lessThan(ArrayList<T> lst, T number) {
ArrayList<T> arLst = new ArrayList<>();
for (T obj: lst) {
if (((Comparable<T>) obj).compareTo(number) < 0)
arLst.add(obj);
}
return arLst;
}
It works as expected, but if I didn't have the Java pre-compiler telling me to cast obj to type Comparable, this wouldn't have run. I'm assuming that the number parameter gets cast in the Comparable version of compareTo. So when I add an obj to my arLst, the object should get cast back into type T, right?
Also, are there any simpler ways to compare wrapper class objects of unknown type T?
You can enforce that T also extends Comparable<? super T>:1
public static <T extends Number & Comparable<? super T>>
ArrayList<T> lessThan(ArrayList<T> lst, T number) {
...
}
Then you can remove the cast.
1. See e.g. Explanation of generic <T extends Comparable<? super T>> in collection.sort/ comparable code? for why we don't just use Comparable<T>.

Can natural ordering be enforced via Java generic type system?

TreeSet(Collection<? extends E> c) constructor was defined as:
Constructs a new tree set containing the elements in the specified collection, sorted according to the natural ordering of its elements. All elements inserted into the set must implement the Comparable interface. Furthermore, all such elements must be mutually comparable: e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the set.
Is it possible syntactically enforce that E in Collection<? extends E> implements Comparable? In above JavaDoc this check postponed to execution time...
Yes, but not with constructors; you'd have to expose a factory method, which can impose constraints on the collection type beyond those imposed by the class as a whole. For example, they could've written
public class TreeSet<E> { ...
public static <E extends Comparable<? super E>> TreeSet<E>
create(Collection<? extends E> collection) {
TreeSet<E> set = new TreeSet<E>();
set.addAll(collection);
return set;
}
...
}
You can make your project require this by using a factory method like this:
public static <T extends Comparable<? super T>> SortedSet<T> safeSortedSet() {
return new TreeSet<T>();
}
Then is code:
Set<String> pass = safeSortedSet();
Set<Foo> fail = safeSortedSet();
Second line produces compile error:
Error:(99, 38) java: incompatible types: inference variable T has incompatible bounds
equality constraints: Test.Foo
upper bounds: java.lang.Comparable
From Tavian Barnes comment:
They could have done class TreeSet<E extends Comparable<? super E>>, but then you wouldn't have been able to use custom comparators for unorderable types. I can't think of a good way to enforce this only on a single constructor
So it is possible with <E extends Comparable<? super E>> syntax, but unfortunately each generic extends / super syntax make ultimate restriction on any other type declaration with E.
In case TreeSet look to Creating a TreeSet with a non-Comparable class: why a run-time exception, rather than compile-time error?
Thanks for all who helps with answer!

Collections.sort() declaration: why <? super T> rather than <T>

Why does Collections.sort(List<T>) have the signature :
public static <T extends Comparable<? super T>> void sort(List<T> list)
and not :
public static <T extends Comparable<T>> void sort(List<? extends T> list)
I understand that they both would serve the same purpose; so why did the framework developers use the first option?
Or are these declarations really different?
Your proposed signature would probably work in Java-8. However in previous Java versions type inference was not so smart. Consider that you have List<java.sql.Date>. Note that java.sql.Date extends java.util.Date which implements Comparable<java.util.Date>. When you compile
List<java.sql.Date> list = new ArrayList<>();
Collections.sort(list);
It perfectly works in Java-7. Here T is inferred to be java.sql.Date which is actually Comparable<java.util.Date> which is Comparable<? super java.sql.Date>. However let's try your signature:
public static <T extends Comparable<T>> void sort(List<? extends T> list) {}
List<java.sql.Date> list = new ArrayList<>();
sort(list);
Here T should be inferred as java.util.Date. However Java 7 specification does not allow such inference. Hence this code can be compiled with Java-8, but fails when compiled under Java-7:
Main.java:14: error: method sort in class Main cannot be applied to given types;
sort(list);
^
required: List<? extends T>
found: List<Date>
reason: inferred type does not conform to declared bound(s)
inferred: Date
bound(s): Comparable<Date>
where T is a type-variable:
T extends Comparable<T> declared in method <T>sort(List<? extends T>)
1 error
Type inference was greatly improved in Java-8. Separate JLS chapter 18 is dedicated to it now, while in Java-7 the rules were much simpler.
// 0
public static <T extends Comparable<? super T>> void sort0(List<T> list)
// 1
public static <T extends Comparable<T>> void sort1(List<? extends T> list)
These signatures differ because they impose different requirements on the relationship between type T and the type argument to Comparable in the definition of T.
Suppose for example that you have this class:
class A implements Comparable<Object> { ... }
Then if you have
List<A> list = ... ;
sort0(list); // works
sort1(list); // fails
The reason sort1 fails is that there is no type T that is both comparable to itself and that is, or is a supertype of, the list's type.
It turns out that class A is malformed, because objects that are Comparable need to meet certain requirements. In particular reversing the comparison should reverse the sign of the result. We can compare an instance of A to an Object but not vice-versa, so this requirement is violated. But note that this is a requirement of the semantics of Comparable and is not imposed by the type system. Considering only the type system, the two sort declarations really are different.
They differ because ? super T is less restrictive than T. It is a Lower Bounded Wildcard (the linked Java Tutorial says, in part)
The term List<Integer> is more restrictive than List<? super Integer> because the former matches a list of type Integer only, whereas the latter matches a list of any type that is a supertype of Integer.
Replace Integer with T and it means a T or a java.lang.Object.

Use generic to store common supertype in Java

Suppose I have a method "mix" that takes two Lists of possibly different types T and S and returns a single List containing the elements of both. For type-safety, I'd like to specify that the returned List is of a type R, where R is a supertype common to both T and S. For example:
List<Number> foo = mix(
Arrays.asList<Integer>(1, 2, 3),
Arrays.asList<Double>(1.0, 2.0, 3.0)
);
To specify this, I could declare the method as
static <R, T extends R, S extends R> List<R> mix(List<T> ts, List<S> ss)
But what if I want to make mix an instance method instead of static, on the class List2<T>?
<R, T extends R, S extends R> List<R> mix ...
shadows the <T> on the instance of List2, so that's no good.
<R, T extends S&T, S extends R> List<R> mix ...
solves the shadowing problem, but isn't accepted by the compiler
<R super T, S extends R> List<R> mix ...
is rejected by the compiler because lower-bounded wildcards can't be stored in a named variable (only used in ? super X expressions)
I could move the arguments to the class itself, like List2<R, T extends R, S extends R>, but the type information really has no business being on the instance level, because it's only used for one method call, and you would have to re-cast the object every time you wanted to invoke the method on different arguments.
As far as I can tell, there's no way to do this with generics. The best I can do would be to return a raw List2 and cast it at the callsite, like before generics were introduced. Does anybody have a better solution?
As noted in the question and in the comments, the following signature would be ideal:
<R super T, S extends R> List<R> mix(List<S> otherList)
But of course, R super T is not allowed by the language (note that polygenelubricants's answer on the linked post is wrong - there are use cases for this syntax, as your question demonstrates).
There's no way to win here - you only have one of several workarounds to choose from:
Resort to using a signature with raw types. Don't do this.
Keep mix a static method. This is actually a decent option, unless it needs to be part of your class's interface for polymorphism-related reasons, or you plan for mix to be such a commonly used method that you think keeping it static is unnacceptable.
Settle with the signature of mix being overly restrictive, and document that certain unchecked casts will be necessary on the part of the caller. This is similar to what Guava's Optional.or had to do. From that method's documentation:
Note about generics: The signature public T or(T defaultValue) is overly restrictive. However, the ideal signature, public <S super T> S or(S), is not legal Java. As a result, some sensible operations involving subtypes are compile errors:
Optional<Integer> optionalInt = getSomeOptionalInt();
Number value = optionalInt.or(0.5); // error
As a workaround, it is always safe to cast an Optional<? extends T> to Optional<T>. Casting [the above Optional instance] to Optional<Number> (where Number is the desired output type) solves the problem:
Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
Number value = optionalInt.or(0.5); // fine
Unfortunately for you, it's not always safe to cast List2<? extends T> to List2<T>. For example, casting a List2<Integer> to a List2<Number> could permit a Double to be added to something that was only supposed to hold Integers and lead to unexpected runtime errors. The exception would be if List2 was immutable (like Optional), but this seems unlikely.
Still, you could get away with such casts if you were careful and documented type-unsafe code with explanations. Assuming mix had the following signature (and implementation, for fun):
List<T> mix(final List<? extends T> otherList) {
final int totalElements = (size() + otherList.size());
final List<T> result = new ArrayList<>(totalElements);
Iterator<? extends T> itr1 = iterator();
Iterator<? extends T> itr2 = otherList.iterator();
while (result.size() < totalElements) {
final T next = (itr1.hasNext() ? itr1 : itr2).next();
result.add(next);
final Iterator<? extends T> temp = itr1;
itr1 = itr2;
itr2 = temp;
}
return result;
}
Then you might have the following call site:
final List2<Integer> ints = new List2<>(Arrays.asList(1, 2, 3));
final List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);
final List<Number> mixed;
// type-unsafe code within this scope
{
#SuppressWarnings("unchecked") // okay because intsAsNumbers isn't written to
final List2<Number> intsAsNumbers = (List2<Number>)(List2<?>)ints;
mixed = intsAsNumbers.mix(doubles);
}
System.out.println(mixed); // [1, 1.5, 2, 2.5, 3, 3.5]
Again, a settling for a static mix is going to be cleaner and have no risk to type-safety. I would make sure to have very good reasons not to keep it that way.
The only thing I'm not sure in your question is whether you already know of which supertype these subclasses extends, or you want a completely generic method where you'd pass two subtypes of any given superclass.
In the first case, I did something similar recently, with an Abstract Class and several subtypes:
public <V extends Superclass> List<Superclass> mix(List<V> list1, List<V> list2) {
List<Superclass> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
}
The latter case is much more complicated. I'd suggest you rethink your design, since it makes much more sense for the mix method to be in the Superclass or in a class which knows the superclass and its subtypes, since you're returning a List of the Superclass.
If you really want to do this, you would have to refactor List2 to List2 and do the following:
public <R, V extends R> List<R> mix(List<V> list1, List<V> list2) {
List<R> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
return mixedList;
}

Can all wildcards in Java be replaced by non-wildcard types?

I can't find any example where wildcards can't be replaced by a generic.
For example:
public void dummy(List<? extends MyObject> list);
is equivalent to
public <T> void dummy(List<T extends MyObject> list);
or
public <T> List<? extends T> dummy2(List<? extends T> list);
is equivalent to
public <T, U> List<U extends T> dummy(List<U extends T> list);
So I don't undertand why wildcard was created as generics is already doing the job. Any ideas or comments?
Nope, it is not always replaceable.
List<? extends Reader> foo();
is not equivalent to
<T> List<T extends Reader> foo();
because you don't know the T when calling foo() (and you cannot know what List<T> will foo() return. The same thing happens in your second example, too.
The demonstration of using wildcards can be found in this (my) answer.
An easy answer is that, deeper level wildcards can't be replaced by a type variable
void foo( List<List<?>> arg )
is very different from
<T>
void foo( List<List<T>> arg)
This is because wildcard capture conversion is only applied to 1st level wildcards. Let's talk about these.
Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.
Since a type variable created by compiler for capture conversion is not accessible to programmer, this has the restricting effect mentioned by #josefx. For example, compiler treats a List<?> object as a List<W> object; since W is internal to compiler, although it has a method add(W item), there's no way for programmer to invoke it, because he has no item of type W. However, if programmer "manually" converts the wildcard to a type variable T, he can have an item with type T and invoke add(T item) on it.
Another rather random case where wildcard can't be replaced type variable:
class Base
List<? extends Number> foo()
class Derived extends Base
List<Integer> foo()
Here, foo() is overriden with a covariant return type, since List<Integer> is a subtype of List<? extends Number. This won't work if the wildcard is replaced with type variable.
There is a usage difference for your examples.
public <T> List<? extends T> dummy2(List<? extends T> list);
returns a list that can contain an unknown subtype of T so you can get objects of type T out of it but can't add to it.
Example T = Number
List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
//Valid since the values are guaranteed to be a subtype of Number
Number o = l.get(0);
//Invalid since we don't know that the valuetype of the list is Integer
l.add(new Integer(1));
So the wildcard can be used to make some operations invalid, this can be used by an API to restrict an interface.
Example:
//Use to remove unliked numbers, thanks to the wildcard
//it is impossible to add a Number
#Override public void removeNumberCallback(List<? extends Number> list){
list.remove(13);
}

Categories

Resources