Java Generics: compareTo and "capture#1-of ?" - java

The following gives me an error message:
public static List<Comparable<?>> merge(Set<List<Comparable<?>>> lists) {
List<Comparable<?>> result = new LinkedList<Comparable<?>>();
HashBiMap<List<Comparable<?>>, Integer> location = HashBiMap.create();
int totalSize;
for (List<Comparable<?>> l : lists) {
location.put(l, 0);
totalSize += l.size();
}
boolean first;
List<Comparable<?>> lowest; //the list with the lowest item to add
int index;
while (result.size() < totalSize) {
first = true;
for (List<Comparable<?>> l : lists) {
if (! l.isEmpty()) {
if (first) {
lowest = l;
}
else if (l.get(location.get(l)).compareTo(lowest.get(location.get(lowest))) <= 0) { //error here
lowest = l;
}
}
}
index = location.get(lowest);
result.add(lowest.get(index));
lowest.remove(index);
}
return result;
}
The error is:
The method compareTo(capture#1-of ?) in the type Comparable<capture#1-of ?> is not applicable for the arguments (Comparable<capture#2-of ?>)
What's going on here? I made the type of everything Comparable so I could call .compareTo and sort this list. Am I using generics incorrectly?

List<?> means "List of anything", so two objects with this type are not the same: One could be a list of String, the other a list of BigDecimal. Obviously, those are not the same.
List<T> means "List of anything but when you see T again, it's the same T".
You must tell the compiler when you mean the same type in different places. Try:
public static <T extends Comparable<? super T>> List<T> merge(Set<List<T>> lists) {
List<T> result = new LinkedList<T>();
HashBiMap<List<T>, Integer> location = HashBiMap.create();
[EDIT] So what does <T extends Comparable<? super T>> List<T> mean? The first part defines a type T with the following properties: It must implement the interface Comparable<? super T> (or Comparable<X> where X is also defined in terms of T).
? super T means that the type which the Comparable supports must T or one of its super types.
Imagine for a moment this inheritance: Double extends Integer extends Number. This is not correct in Java but imagine that Double is just an Integer plus a fraction part. In this scenario, a Comparable which works for Number also works for Integer and Double since both derive from Number. So Comparable<Number> would satisfy the super part for T being Number, Integer or Double.
As long as each of these types support the Comparable interface, they also satisfy the first part of the declaration. This means, you can pass in Number for T and the resulting code will also work when there are Integer and Double instances in the lists. If you Integer for T, you can still use Double but Number is not possible because it doesn't satisfy T extends Comparable anymore (the super part would still work, though).
The next step is to understand that the expression between static and List just declares the properties of the type T which is used later in the code. This way, you don't have to repeat this long declaration over and over again. It's part of the behavior of the method (like public) and not part of the actual code.

Related

Java generic T extends Object

Given the following class:
public class TestMap<K,V> {
private HashMap<K,V> map;
public void put(K k, V v) {
map.put(k, v);
}
public V get(K k) {
return map.get(k);
}
}
and this function:
static<T extends Object> String getObj(TestMap<T, String> m, String e) {
return m.get(e);
}
Why does "get" show this error:
The method get(T) in the type MyHashMap<T,String> is not applicable for the arguments (String)
When getObj states T extends Object, and the map has been initialized with TestMap<T, String> m, why can't String be used as parameter? I cant wrap my head around why this doesnt work, as String is a subtype of Object ?
I tried extending T from a custom superclass and using a subclass instead of String.
get() requires as an argument a key, which is of type T, not String.
So either you need to change e to type T or change the type of the Map, e.g., to TestMap<String, T>.
Generics describe what an Object is, List<String> strings is a list of Strings. That gives us some convenience, we don't have to cast. String s = strings.get(0); and we get some compile-time protection. Integer i = 1; then strings.add(i); fails to compile.
The error you are getting is a compile time protection. The key you're providing is an Object, so it might be the correct type. In the case of a map, "why not check?" It is just going to hashcode and check for equality which is ok. In the case of a List though, strings.add((Object)i); needs to fail.
If the doesn't fail, then later something might call. for(String s: strings) and get a class cast exception.
To fix this you can change you V get(K k) method to a V get(Object k) the same as map uses. Your get method requires type K a map normally has a get method with a Object
As mentioned "T extends Object" is the same as just "T". You use the extends when you want to put a lower bound on something. public <T extends Number> int sum(List<T> numbers). In this case "T" can be any class that extends numbers. We can do Number n = numbers.get(0); because we know it is at least a number. But we could not do numbers.add(n); because while T is at least a number, it could be something else like an Integer, Double, Number, BigDecimal etc.

What's the point of a write-only collection?

<? extends T> makes for a read-only collection
<? super T> makes for a write-only collection
I somehow get why use a read-only collection,for instance to use it in a multithreaded environment (any other cases?)
But why use a write-only collection? What's the point if you cannot read from it and use its values at some point? I know that you can get an Object out of it but that defies type safety.
Edit:
#Thomas the linked question (Difference between <? super T> and <? extends T> in Java) does show how to make a write only collection but does not answer 'why' would you need one in the first place.So it's not a duplicate
Statements like
<? extends T> makes for a read-only collection
<? super T> makes for a write-only collection
are just wrong. Wildcard element types do not say anything about the ability to read or write.
To show counter examples:
static <T> void modify(List<? extends T> l) {
l.sort(Comparator.comparing(Object::toString));
l.remove(l.size() - 1);
Collections.swap(l, 0, l.size() - 1);
l.add(null);
duplicateFirst(l);
}
static <U> void duplicateFirst(List<U> l) {
U u = l.get(0);
l.add(u);
}
shows quite some modifications possible for the List<? extends T>, without problems.
Likewise, you can read a List<? super T>.
static <T> void read(List<? super T> l) {
for(var t: l) System.out.println(t);
}
Usage restrictions imposed by ? extends T or ? super T are only in relation to T. You can not take an object of type T, e.g. from another method parameter, and add it to a List<? extends T>, because the list’s actual type might be a subtype of T. Likewise, you can not assume the elements of a List<? super T> to be of type T, because the list’s actual type might be a supertype of T, so the only assumption you can make, is that the elements are instances of Object, as every object is.
So when you have a method like
public static <T> void copy(List<? super T> dest, List<? extends T> src)
the method can not take elements from dest and add them to src (in a typesafe way), but only the other way round.
It’s important to emphasize that unlike other programming languages, Java has use site variance, so the relationship between the two list described above only applies to the copy method declaring this relationship. The lists passed to this method do not have to be “consumer of T” and “producer of T” throughout their entire lifetime.
So you can use the method like
List<Integer> first = List.of(0, 1, 2, 3, 7, 8, 9);
List<Number> second = new ArrayList<>(Collections.nCopies(7, null));
Collections.copy(second, first);
List<Object> third = new ArrayList<>(Collections.nCopies(11, " x "));
Collections.copy(third.subList(2, 9), second);
System.out.println(third);
Yes, copy was a real life example. Online demo
Note how the second list changes its role from consumer of Integer to producer of Object for the two copy invocations while its actual element type is Number.
Other examples for ? super T
Collections.fill(List<? super T> list, T obj)
Collections.addAll(Collection<? super T> c, T... elements)
To sum it up, in Java, rules like PECS are relevant for the declaration of methods, to determine the (typical) roles of the arguments within the method itself. This raises the flexibility for the caller, as it allows combining different invariant types, like the example of copying from a List<Integer> to a List<Number>.
But never assume that the generic types tell anything about the ability to read or write a collection.
Note that "write only collection" depends on the point of view.
Lets write a method that adds a bunch of numbers to a collection:
public static void addNumbers(List<? super Integer> target, int count) {
for (int i = 0; i < count; i++) {
target.add(i);
}
}
For this method the list target is a write only list: the method can only add numbers to it, it can not use the values that it added to the list.
On the other side there is the caller:
public static void caller() {
List<Number> myList = new ArrayList<>();
addNumbers(myList, 10);
double sum = 0;
for (Number n: myList) {
sum += n.doubleValue();
}
System.out.println(sum);
}
This method works with a specific list (myList) and therefore can read the values that addNumbers stuffed into it.
For this method the list is not a write only list, for this method it is an ordinary list.

Difference between m1 and m2 generic method in Java

Can someone help me to understand the difference between m1() and m2() generic method.
class C1 {
<T extends Number> void m1(List<T> list){
System.out.println(list);
for (T t : list){
System.out.println(t);
}
}
void m2(List<? extends Number> list){
for(Number n :list){
}
}
}
There is no functional difference between these 2 snippets. They do the same thing, are equally flexible, have the same signatures, and basically compile down to the precise same class file.
The first snippet says: There is some type; we restrict it so that it is either java.lang.Number or some subtype thereof. Then, this type shows up twice: Once as the type param on a list (so, it's a list of numbers, or a list of integers, or a list of doubles, etc), and once as the type of the variable when we iterate through each element in the list. All that you can tell about the t variable in for (T t : list) is that it is at the very least java.lang.Number but that's all you know.
In the second snippet you say: We have a list of either java.lang.Number or any subtype thereof. All we do with this list is iterate through it, at which point it's fine to state that you can be sure that the items that fall out when you iterate through this list are at the very least a java.lang.Number.
In both cases, the loop var (T t in the first snippet, Number n in the second) are bound as java.lang.Number and can be used as a number.

compareTo with generic arguments is giving warning

I want to compare two arguments in less method which are of type Comparable. But if I use comapreTo it is giving me error in type casting:
private boolean less(Comparable<T> u, Comparable<T> v) {
return u.compareTo(v) < 0;
}
I fixed the issue by type casting the argument passed into compareTo. But still it is giving me warning (Type safety: Unchecked cast from Comparable to T). Why is it so. Am I doing something wrong. What is the best way to achieve this.
private boolean less(Comparable<T> u, Comparable<T> v) {
return u.compareTo((T) v) < 0;
}
Since u has type Comparable<T>, u.compareTo() takes a T. You're passing it v, a Comparable<T>, which is not compatible.
You might be assuming in your mind that a type that implements Comparable is comparable to itself. But there is no such requirement on a technical level. It's perfectly legal for me to make a type Foo, that is Comparable<Bar>, i.e. it compares to Bar, where Foo and Bar are completely unrelated. Trying to pass a Foo into a Foo object's compareTo will crash.
If you want to make a constraint that the two arguments are of the same type which is comparable to itself, you would do something like
private <T extends Comparable<? super T>> boolean less(T u, T v) {
return u.compareTo(v) < 0;
}
However, for the purposes of this function, the constraint that the two arguments are of the same type is not technically necessary (although it should be true in all normal use cases). And in generics we always want to use the least restrictive bound possible, which is, as other answers have mentioned,
private <T> boolean less(Comparable<T> u, T v) {
return u.compareTo(v) < 0;
}
In your comments, you mentioned you are comparing elements from a Comparable<T>[]. That is not a type of array that allows you to compare elements from. You just know that it's an array of elements that are comparable to a certain T, but not comparable to themselves. Much more useful would be to have a T[], where T has a bound that it is comparable to itself:
class Whatever<T extends Comparable<? super T>> {
//...
T[] yourArray;
}
The initial error is correct, and the warning is correct to: your trick is very unsafe: you can't cast v to T.
Because your method takestwo parameters u and v, each one is an instance of a class, which implements compareTo ( some instance of T).
precisely: u can compare to T, v can compare to T.
But, inside, you want to compare u and v. There is absolutely no garantee you can do it.
This, for example, is correct:
private static <X> boolean less(Comparable<X> u, X v)
{
return u.compareTo(v) < 0;
}

Java Generics - are these two method declarations equivalent?

Given some class SomeBaseClass, are these two method declarations equivalent?
public <T extends SomeBaseClass> void myMethod(Class<T> clz)
and
public void myMethod(Class<? extends SomeBaseClass> clz)
For the caller: yes, they are equivalent.
For the code inside the method: no.
The difference is that within the code of the first example you can use the type T (for example to hold an object created by clz.newInstance()), while in the second you can't.
No, they're not. With the first definition, you can use the type T inside the method definition, e.g. create an ArrayList<T> or return T. With the second definition, that's not possible.
Bounded wildcards are subject to certain restrictions to avoid heap pollution.
When you use the wildcard ? extends X you know you can read generic information, but you cannot write.
For instance
List<String> jedis = new ArrayList<String>();
jedis.add("Obiwan");
List<? extends CharSequence> ls = jedis
CharSequence obiwan = ls.get(0); //Ok
ls.add(new StringBuffer("Anakin")); //Not Ok
The compiler avoided heap pollution when you tried to add a CharSequence (i.e. StringBuffer) to the collection. Because the compiler cannot be sure (due to wildcards) that the actual implementation of the collection is of type StringBuffer.
When you use ? super X you know you can write generic information, but you cannot be sure of the type of what you read.
For instance
List<Object> jedis = new ArrayList<Object>();
jedis.add("Obiwan");
List<? super String> ls = jedis;
ls.add("Anakin"); //Ok
String obiwan = ls.get(0); //Not Ok, we can´t be sure list is of Strings.
In this case, due to wildcards, the compiler knows that the actual implementation of the collection could be anything in the ancestors of String. Thus it cannot guarantee that what you will get will be a String. Right?
This same restrictions are the ones you would be subject too in any declaration with bounded wildcards. These are typically known as the get/put principle.
By using a type parameter T you change the story, from the method standpoint you are not using a bounded wildcard but an actual type and therefore you could "get" and "put" things into instances of the class and the compiler would not complain.
For instance, consider the code in Collections.sort method. If we write a method as follows, we would get a compile error:
public static void sort(List<? extends Number> numbers){
Object[] a = numbers.toArray();
Arrays.sort(a);
ListIterator<? extends Number> i = numbers.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((Number)a[j]); //Not Ok, you cannot be sure the list is of Number
}
}
But if you write it like this, you can do the work
public static <T extends Number> void sort(List<T> numbers){
Object[] a = numbers.toArray();
Arrays.sort(a);
ListIterator<T> i = numbers.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((T)a[j]);
}
}
And you could even invoke the method with collections bounded with wildcards thanks to a thing called capture conversion:
List<? extends Number> ints = new ArrayList<Integer>();
List<? extends Number> floats = new ArrayList<Float>();
sort(ints);
sort(floats);
This could not be achieved otherwise.
In summary, as others said from the caller standpoint they are alike, from the implementation standpoint, they are not.
No. On top of my head, I can think of the following differences:
The two versions are not override-equivalent. For instance,
class Foo {
public <T extends SomeBaseClass> void myMethod(Class<T> clz) { }
}
class Bar extends Foo {
public void myMethod(Class<? extends SomeBaseClass> clz) { }
}
does not compile:
Name clash: The method myMethod(Class) of type Bar has the same erasure as myMethod(Class) of type Foo but does not override it
If a type parameter appears more than once in a method signature, it always represents the same type, but if a wildcard appears more than once, each occurrence may refer to a different type. For instance,
<T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
compiles, but
Comparable<?> max(Comparable<?> a, Comparable<?> b) {
return a.compareTo(b) > 0 ? a : b;
}
does not, because the latter may be called by
max(Integer.MAX_VALUE, "hello");
The method body may refer to the actual type used by the caller using a type parameter, but not using a wildcard type. For instance:
<T extends Comparable<T>> T max(T... ts) {
if (ts.length == 0) {
return null;
}
T max = ts[0];
for (int i = 1; i < ts.length; i++) {
if (max.compareTo(ts[i]) > 0) {
max = ts[i];
}
}
return max;
}
compiles.
#Mark #Joachim #Michael
see the example in JLS3 5.1.10 Capture Conversion
public static void reverse(List<?> list) { rev(list);}
private static <T> void rev(List<T> list){ ... }
so the <?> version can do anything the <T> version can do.
this is easy to accept if the runtime is reified. a List<?> object must be a List<X> object of some specific non-wildcard X anyway, and we can access this X at runtime. So there's no difference using a List<?> or a List<T>
With type erasure, we have no access to T or X, so there's no difference either. We can insert a T into a List<T> - but where can you get a T object, if T is private to the invocation, and erased? There are two possibilities:
the T object is already stored in the List<T>. so we are manipulating elements themselves. As the reverse/rev example shows, there's no problem doing this to List<?> either
it comes out-of-band. There's other arrangement made by the programmer, so that an object somewhere else is guaranteed to be of type T for the invocation. Unchecked casting must be done to override compiler. Again, no problem to do the same thing to List<?>

Categories