Signature of Collections.min/max method - java

In Java, the Collections class contains the following method:
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> c)
Its signature is well-known for its advanced use of generics,
so much that it is mentioned in the Java in a Nutshell book
and in the official Sun Generics Tutorial.
However, I could not find a convincing answer to the following question:
Why is the formal parameter of type Collection<? extends T>, rather
than Collection<T>? What's the added benefit?

Type inference is a tricky topic that I'll admit that I don't know that much about. However, examine this example:
public class ScratchPad {
private static class A implements Comparable<A> {
public int compareTo(A o) { return 0; }
}
private static class B extends A {}
private static class C extends B {}
public static void main(String[] args)
{
Collection<C> coll = null;
B b = Scratchpad.<B>min(coll);
}
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> c) {
return null;
}
//public static <T extends Object & Comparable<? super T>> T min(Collection<T> c) {
// return null;
//}
}
Consider that the first signature of min() allows the call to compile whereas the second does not. This isn't a very practical example, since one must ask why I would be explicitly typing the method to <B>, but perhaps there is an implicit inference where B would be the inferred type.

One benefit of the ? is that it prohibits additions of items to the Collection

I think it actually doesn't give you anything more for this method, however its a good habit to get into when T is part of the class and not just a static method.
They are including it here so it can become the new convention where every generic should be extended by ?
A class of T should follow PECS: What is PECS (Producer Extends Consumer Super)?
But a static method doesn't need to (at least the parameters, the return value should always)

This is to support a legacy signature of the method in Java 1.4 ( and before ).
Prior to Java 5 the signature for these methods was
public static Object min ( Collection c );
With multiple bounds the erasure rules make the first bound the raw type of the method, so without Object & the signature would be
public static Comparable min ( Collection c );
and legacy code would break.
This is taken from O'Reilly's Java Generics and Collections book, chapter 3.6

Building on the comments I put on Mark's answer, if you have something like
class Play {
class A implements Comparable<A> {
#Override
public int compareTo(A o) {
return 0;
}
}
class B extends A {
}
class C extends A {
}
public static <T extends Object & Comparable<? super T>> T min(
Collection<? extends T> c) {
Iterator<? extends T> i = c.iterator();
T candidate = i.next();
while (i.hasNext()) {
T next = i.next();
if (next.compareTo(candidate) < 0)
candidate = next;
}
return candidate;
}
public static List<? extends A> getMixedList() {
Play p = new Play();
ArrayList<A> c = new ArrayList<A>();
c.add(p.new C());
c.add(p.new B());
return c;
}
public static void main(String[] args) {
ArrayList<A> c = new ArrayList<A>();
Collection<? extends A> coll = getMixedList();
A a = Play.min(coll);
}
}
It's clearer that min returns an object of type A (the actual signature is <A> A Play.min(Collection<? extends A> c) ). If you leave min(Collection<T>) without the extends part then Play.min(coll) will have the following signature <? extends A> ? extends A Play.min(Collection<? extends A> c) which isn't as clear.

Related

Do these two Java generic methods accept the same data types?

I'm new to Java and I'm trying to learn about generics. I tried to implement a simple version of binarySearch() method that is also found in the Collections class. I looked up the method signature and it's something like this:
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) {
// definition
}
I was wondering if the method above still accepts the same data types if you were to change the method definition to this:
public static <T extends Comparable<? super T>> int binarySearch(List<T> list, T key) {
// definition
}
If not, what are the differences between the two? Thank you!
Consider these classes:
class A extends Comparable<A> { /* ... */ }
class B extends A { /* ... */ }
Now define a key and a list with these types:
A key = new B();
List<B> list = List.of(key);
You can invoke the first form with these arguments, but not the second.
For example:
static class NonComparable {
}
static class MyComparable implements Comparable<NonComparable> {
#Override
public int compareTo(NonComparable o) {
return 0; // irrelevant for the example
}
}
And then declare the parameters:
List<MyComparable> list = Arrays.asList(new MyComparable());
NonComparable nonComparable = new NonComparable();
binarySearch(list, nonComparable);
One of your method definitions allows an invocation, the other does not.

Java Generic Comparator

public class arr<T>
{
class comp <T extends Comparable<T>> implements Comparator<T>
{
public int compare(T lObj,T rObj)
{
return lObj.compareTo(rObj);
}
}
ArrayList<T> list;
Comparator<T> comparator;
public arr()
{
list = new ArrayList<T>();
comparator = new comp();
}
public void add(T data)
{
list.add(data);
}
public int getLength()
{
return list.size();
}
public T get(int index)
{
return list.get(index);
}
public void sort()
{
list.sort(comparator);
}
}
Hello, I am trying to make the sort function work but have a problem.
In the arr constructor, if I write
comparator = new comp<T>();
it gives me an error saying
"type argument T#1 is not within bounds of type-variable T#2 comparator =
new comp<T>(); ^
where T#1,T#2 are type-variables:
T#1 extends Object declared in class arr
T#2 extends Comparable<T#2> declared in class arr.comp"
And if I take out the type and write like this
comparator = new comp;
then it does work but gives me a warning that says
warning: [rawtypes] found raw type: arr.comp
comparator = new comp();
I can see what it means by raw types. I am not specifying the type, but somehow it works and if I try to fix the warning by specifying the type then, it throws an error. Could you please help me figure it out? I know... I am a noob my code must be a pain in your eyes. I am playing with generic comparators and trying many things to get familiar. Thank you.
Your code is confusing you, because the T defined by comp is hiding the T defined by arr. For the explanation below, I'll call them Tcomp and Tarr.
Tcomp is required to extend Comparable, but Tarr isn't required to do so, which means that Tarr cannot be "mapped" to Tcomp.
To fix, change Tarr so it is also required to extend Comparable:
public class arr<T extends Comparable<T>>
On a side note:
You comp class is an inner class, but it doesn't use anything from the outer class, so it should be a static nested class:
static class comp<T extends Comparable<T>> implements Comparator<T>
Alternatively, leave comp as an inner class, and let it reuse the T from the outer class:
class arr<T extends Comparable<T>>
{
class comp implements Comparator<T>
But, since Java (8 or higher) comes with an implementation of Comparator for comparing Comparable objects, you should use it:
public class arr<T extends Comparable<T>>
{
ArrayList<T> list;
Comparator<T> comparator;
public arr()
{
list = new ArrayList<T>();
comparator = Comparator.naturalOrder();
}
// rest of code
}

Incompatible types when using upper bounding wildcard

I'm really confused of how upper bounded types work in Java generics.
Let's say I have
interface IModel<T>
interface I
class A implements I
class B implements I
class C implements I
then I have a method with parameter as follows
foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
calling that method like
IModel<Map<A, Map<B, List<C>>>> model = ...
foo(model)
ends with compilation error
Error:(112, 49) java: incompatible types: IModel<java.util.Map<A,java.util.Map<B,java.util.List<C>>>> cannot be converted to IModel<java.util.Map<? extends I,java.util.Map<? extends I,java.util.List<? extends I>>>>
I have read docs about Java generics from the Oracle web, trying to google it, but there must be something I totally misunderstood.
This question can be shorted as why
foo(IModel<List<? extends I>> dataModel)
can not accept argument like
IModel<List<A>> model
Explanation
List<A> is a subtype of List<? extends I>, so it is ok:
public void bar(List<? extends I> list);
List<A> listA;
bar(listA);
But, it does not make IModel<List<A>> a subtype of IModel<List<? extends I>>, just like IModel<Dog> is not a subtype of IModel<Animal>, so the code you posted can not be compiled.
Solution
You can change it to:
foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
or
<FIRST extends I, SECOND extends I, THIRD extends I> void foo(IModel<Map<FIRST, Map<SECOND, List<THIRD>>>> dataModel)
to make it compile.
First of all, I wonder how much effort it would have been for you (one person) to sort out the code to be in this form:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
{
}
}
instead of letting hundreds of people (who want to help you) do this on their own, in order to have something that they can compile and have a look at in their IDE. I mean, it's not that hard.
That being said: Technically, you're missing a few more extends clauses here. This compiles fine:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
{
}
}
But you should
not
implement it like that. That's obscure. Whatever this dataModel parameter is, you should consider creating a proper data structure for that, instead of passing along such a mess of deeply nested generic maps.
The reason of why the original version did not compile was already mentioned in other answers. And it can be made clearer by showing an example using a much simpler method call. Consider this example:
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
List<List<A>> lists = null;
exampleA(lists); // Error
exampleB(lists); // Works!
}
static void exampleA(List<List<? extends I>> lists)
{
}
static void exampleB(List<? extends List<? extends I>> lists)
{
}
}
The exampleA method cannot accept the given list, whereas the exampleB method can accept it.
The details are explained nicely in Which super-subtype relationships exist among instantiations of generic types? of the generics FAQ by Angelika Langer.
Intuitively, the key point is that the type List<A> is a subtype of List<? extends I>. But letting the method accept only a List<List<? extends I>> does not allow you to pass in a list whose elements are subtypes of List<? extends I>. In order to accept subtypes, you have to use ? extends.
(This could even be simplified further: When a method accepts a List<Number>, then you cannot pass in a List<Integer>. But this would not make the point of List<A> being a subtype of List<? extends I> clear here)
Having a method method1(Map<I> aMap>) and A being a class implementing I doesn't allow you to call the method with a Map<A> and that's for a reason.
Having the method:
public static void foo2(IModel<I> dataModel) {
System.out.println("Fooing 2");
}
Imagine this code:
IModel<A> simpleModel = new IModel<A>() {};
foo2(simpleModel);
This shouldn't work because you supply a more specific type to a method that requires a generic type. Now imagine foo2 does the following:
public static void foo2(IModel<I> dataModel) {
dataModel = new IModel<B>() {};
System.out.println("Fooing 2 after we change the instance");
}
Here you will try to set IModel to IModel which is valid - because B extends I, but if you were able to call that method with IModel it wouldn't work
Create your model like:
IModel<Map<I, Map<I, List<I>>>> model = ...
and in the corresponding maps and lists add objects of type A, B and C which will be valid and then call the function foo(model)

Java generics - cast assignable capture type to subclass

I have the following scenario in Java generics:
public abstract class A<T> {
protected final Class<T> typeOfX;
public A(final Class<T> typeOfX) {
this.typeOfX = typeOfX;
}
public abstract void load(final T x);
}
public class AnyA<S> extends A<S> {
private final Map<String, A<? extends S>> map;
public AnyA(final Class<S> superTypeOfX,
final Map<String, A<? extends S>> map) {
super(superTypeOfX);
this.map = map;
}
#Override
public void load(final S superx) {
for (final A<? extends S> a: map.values())
if (a.typeOfX.isAssignableFrom(superx.getClass())) //Here I want to say: "if superx can be casted to a.typeOfX".
a.load(a.typeOfX.cast(superx)); //Here I want to cast superx to a.typeOfX (so as to call the load method). Here's the compile error.
}
}
I'm getting the error:
incompatible types: S cannot be converted to CAP#1
where S is a type-variable:
S extends Object declared in class AnyA
where CAP#1 is a fresh type-variable:
CAP#1 extends S from capture of ? extends S
AnyA is a composite A, i.e. is an A which maintains several other A instances.
AnyA in its load(...) method shall decide which of the maintained A instances should be used to "pass the loading process to" of the argument.
In other words, AnyA is responsible for finding the correct A to load the argument.
But also AnyA is an A because it handles loading the argument.
My question is:
Why is this cast not possible, by the time I know that S is a sub-class of T and all A instances in AnyA can load a subclass of S?
How can I overcome this problem without changing the class diagram too much?
I have read about "helper methods" but cannot match the example shown there to my problem.
I'm using NetBeans IDE with Java SDK 8.
Note that regardless of what you do, the code is not "syntactically type safe" in any case. There is an unchecked cast, and the only safety belt that prevents this from going wrong is the isAssignableFrom check.
(That is often OK, I'm just mentioning it for completeness)
The reason for the error may be more obvious when you pull the lines apart (here, S stands for SuperType, according to the Type Parameter Naming Conventions - please follow them!)
A<? extends S> a = ...;
S s = a.typeOfX.cast(s);
a.load(s);
The A<? extends S> intuitively means that it is an A that can accept an unknown type in its load method. You know that it extends type S, but you do not know which type this is.
It may become blatantly obvious when you insert Object for S:
A<String> specificA = ...;
// So the "specificA" can load "String" objects. Then this is fine:
A<? extends Object> a = specificA;
Object s = a.typeOfX.cast(s);
// But here's the error: "s" is only an Object, and not a String!
a.load(s);
I think the main point of confusion (and the main reason for the question) was the following: When calling
Object s = a.typeOfX.cast(s);
and typeOfX is String.class, then the return type of the cast will not be String, but only the type that the compiler can infer at this point. And this is Object, in the example above.
However, you already referred to the Helper Methods, and indeed, with some trickery, you can make this compile,
but... (see notes below)
import java.util.Map;
abstract class A<T>
{
protected final Class<T> typeOfX;
public A(Class<T> typeOfX)
{
this.typeOfX = typeOfX;
}
public abstract void load(T x);
}
class AnyA<S> extends A<S>
{
private final Map<String, A<? extends S>> map;
public AnyA(Class<S> superTypeOfX,
Map<String, A<? extends S>> map)
{
super(superTypeOfX);
this.map = map;
}
#Override
public void load(S s)
{
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(s.getClass()))
{
callLoad(a, s);
}
}
}
private static <S, T extends S> T cast(A<T> a, S s)
{
T t = a.typeOfX.cast(s);
return t;
}
private static <T, S extends T> void callLoad(A<S> a, T s)
{
a.load(cast(a, s));
}
}
I would not recommend this in practice.
Personally and subjectively: I think that when you are doing the isAssignableFrom check, then the (unchecked) cast should be as close as possible to this check. Otherwise, the code will be very hard to understand.
So although unchecked casts are a code smell in practice, and I try to avoid SuppressWarning whenever possible, I would consider this as far more readable:
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(superx.getClass()))
{
// This call is safe as of the check done above:
#SuppressWarnings("unchecked")
A<Object> castA = (A<Object>) a;
castA.load(superx);
}
}

Using generics in Comparable

I am trying to implement generics in Java using Comparable<T> interface.
public static <T> T[] sort(T[] a) {
//need to compare 2 elements of a
}
Let's say, I want to override the compareTo method for the above type T in the Comparable interface. I.e. I need to compare two elements of my type T, how will I do it? I don't know what my T type will be.
You need to set a type constraint on your method.
public static <T extends Comparable<? super T>> T[] sort (T[] a)
{
//need to compare 2 elements of a
}
This forces the type T to have the compareTo(T other) method. This means you can do the following in your method:
if (a[i].compareTo(a[j]) > 0) }
}
Try using <T extends Comparable<T>> and then compareTo
Old question but...
As jjnguy responded, you need to use:
public static <T extends Comparable<? super T>> T[] sort(T[] a) {
...
}
Consider the following:
public class A implements Comparable<A> {}
public class B extends A {}
The class B implicitly implements Comparable<A>, not Comparable<B>, hence your sort method could not be used on an array of B's if used Comparable<T> instead of Comparable<? super T>. To be more explicit:
public static <T extends Comparable<T>> T[] brokenSort(T[] a) {
...
}
would work just fine in the following case:
A[] data = new A[3];
...
data = brokenSort(A);
because in this case the type parameter T would be bound to A. The following would produce a compiler error:
B[] data = new B[3];
...
data = brokenSort(B);
because T cannot be bound to B since B does not implement Comparable<B>.

Categories

Resources