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.
Related
<? 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.
This question already has answers here:
Generics <? super A> doesn't allow superTypes of A to be added to the list
(2 answers)
Closed 4 years ago.
I am trying out a easy to understand example about contravariance in Java and having a issue understanding.
In the below example I have List<? super CarBill> list1 . My understanding is i should be able to add an object of any superclass of CarBill. By that logic i should be able to add objects of Bill class to it too right ?
I get a compilation error.
package Generics;
import java.util.ArrayList;
import java.util.List;
public class VarianceTests {
static class Bill{
String vName;
String type;
Bill(String vName){
this.vName=vName;
}
Bill(String vName,String type){
this.vName=vName;
this.type=type;
}
}
static class CarBill extends Bill{
String name;
CarBill(String name)
{
super(name,"Car");
}
}
static class Car<T extends Bill> {
T car;
Car(T car){
this.car=car;
}
String getNameOfCar() {
return car.vName;
}
}
public static void main(String args[]) {
CarBill cBill = new CarBill("Baleno");
Bill bill=new Bill("Whatever");
Car car = new Car(bill); //cBill is valid too as Car accepts <? extends Bill>
List<? super CarBill> list1 = new ArrayList<>();
list1.add(cBill);
list1.add(bill);
}
public void acceptListOfCars(List<? extends Bill> list1) {
Bill b = list1.get(0); //Valid syntax
}
}
Your understanding is mistaken.
List<? super CarBill> means that the list can be a list of any super class of CarBill or CarBill itself. It could be List<Object>, it could be List<Bill>, it could even be List<CarBill>. Which one is it actually? We don't know.
Therefore, you can't add a Bill to a List<? super CarBill> because what if the list is actually a List<CarBill>? You can't add a Bill to a List<CarBill>.
In other words, you can only add CarBill or subclasses of CarBill into a List<? super CarBill>.
If your intention is to create a list that can store any type of Bill, you can create a List<Bill>.
This post might help as well.
Not quite.
Let's start with this code:
List<Integer> listOfInts = new ArrayList<Integer>();
List<Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
The above code won't in fact compile; the second line is an invalid assignment. Your line of thinking would say: But.. why? Number is a supertype of Integer, so, a list of integers is trivially also a list of numbers, no? but then the third line shows why this line of reasoning is incorrect. Java will NOT let you write the above code. What you CAN write is this: The same thing, but this time we tweak the second line:
List<Integer> listOfInts = new ArrayList<Integer>();
List<? extends Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
This time, you get a compiler error on the third line: You cannot add a double to this list. But, if you read from it, you'd get numbers out (not objects). This is all good: The above snippet of code should never compile no matter what we try because it tries to add doubles to a list of ints.
The point is: List<? extends Number> does not mean: "This list contains numbers, or any subtypes thereof". No; just like List x = new ArrayList() is legal java, List<Number> means 'this list contains numbers or any subtypes thereof' because any instance of any subtype of number can itself be used as a Number. List<? extends Number> means: This is a list restrained to contain only instances of some specific type, but which type is not known. What IS known, is that whatever that type is, it's either Number or some subtype thereof.
Hence, you can't add ANYTHING to a List<? extends Number>.
For super, a similar story:
List<? super CarBill> means: This is a list that is restricted to contain only instances of some specific type, but which type is not known. What IS known, is that, whatever type it is, it is either CarBill or some SUPERtype thereof.
The upside of doing this, is that you can add CarBill instances to a List<? super CarBill> variable. When you read from it, you'll get objects out.
My understanding is i should be able to add an object of any superclass of CarBill
No.
A List<? super CarBill> is not a list that will accept objects of any supertype of CarBill. It's a list that will accept objects of some particular supertype of CarBill, but which supertype it is is unknown.
You can add any object of type CarBill, because that is guaranteed be a subtype of type ?. But a supertype of CarBill is not guaranteed to be a subtype of ?.
For instance:
List<? super CarBill> myList = new ArrayList<Bill>();
Object o = "Anything";
Object is a supertype of CarBill. So if you could add any supertype of CarBill to the list, you would be able to add o to the list, which would mean you could add anything to the list.
I have written below code
class Student {}
class Student1 extends Student {}
class Student2 extends Student {}
List<? extends Student> emp = new ArrayList<>();
emp.add(new Student()); // I do not want this to happen. at compile time it should give me error
emp.add(new Student1()); // this should happen
emp.add(new Student2()); // this should happen
But in above code its not working throwing compile error in all 3 adds.
Any pointers?
No, there is no such option in Java to constrain the type parameter to just subtypes of a given type. It looks like you have taken the meaning of the syntax ? extends Type a bit too literally. Type there is the upper bound of the wildcard, and the bounds are always inclusive.
If your idea was the way it really worked, there would be no type by which you could refer to a general item of your list: the narrowest such type is Student.
In java Generics are not co-variant. So List<T> is not a sub-type of List<S> if T is a sub-type of S. This was done to ensure static, compile time type safety. If it were allowed you could do things like following:
List<Integer> intList = new ArrayList<>();
List<Number> numberList = intList;
numberList.add(1.0); // Breaks the contract that intList will contain only Integers
// (or Objects of classes derived from Integer class)
Now to allow functions which could work on Lists containing anything which extends some base class, Bounded wildcards were introduced. So for example, if you wish to write a generic add method which returns the sum of all elements in a List (irrespective of whether the list if of Type Integer, Double, Float), you can write the following code
double add(List<? extends Number> numberList) {
double ans = 0;
for (Number num : numberList) {
ans += num.doubleValue();
}
return ans;
}
The argument can be List of any Object which extends Number such as List<Double>, List<Float>, List<Short>, List<Integer>.
Now coming to your question, when you say a List is of Type <? extends Student> and add Student1() to it, the compiler is not able to verify whether the type is correct or not and it breaks the compile time safety. (Because ? is an unknown type). Normally you cannot add anything to a List having a bounded wildcard as its type (except null and except if you follow a rather complicated process). So either you can declare your list as of type Student but that will mean that your first add statement would not throw an error.
In most practical scenarios, this kind of case is handled by making Student abstract and implementing all the common functionality in the abstract class while declaring your list as of type Student.
Also if you ever want to add to a bounded wildcard type, you can write a helper function like this and call it in your method (This also accepts Student as valid type. There's no way to force a Type to extend something because type resolution is always inclusive in Java):
private <Student, S extends Student> void addd(List<Student> l, S element) {
l.add(element);
}
and call it as:
addd(emp, new Student());
addd(emp, new Student1());
addd(emp, new Student3());
Use "List<? extends Student>" when you want to access the existing elements of List.
Use "List<? super Student>" when you want to set the elements to the List.
I don't think I really understand Java generics. What's the difference between these two methods? And why does the second not compile, with the error shown below.
Thanks
static List<Integer> add2 (List<Integer> lst) throws Exception {
List<Integer> res = lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
.
static <T extends List<Integer>> T add2 (T lst) throws Exception {
T res = lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - incompatible types
required: T
found: capture#1 of ? extends java.util.List
For the second method to compile, you have to cast the result of newInstace() to T:
static <T extends List<Integer>> T add2 (T lst) throws Exception {
T res = (T) lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
Regarding the difference between the two methods, let's forget about the implementation, and consider only the signature.
After the code is compiled, both methods will have exactly the same signature (so the compiler would give an error if the have the same name). This happens because of what is called type erasure.
In Java, all the type parameters disappear after compilation. They are replaced by the most generic possible raw type. In this case, both methods will be compiled as List add2(List).
Now, this will show the difference between the two methods:
class Main {
static <T extends List<Integer>> T add1(T lst) { ... }
static List<Integer> add2(List<Integer> lst) { ... }
public static void main(String[] args) {
ArrayList<Integer> l = new ArrayList<Integer>();
ArrayList<Integer> l1 = add1(l);
ArrayList<Integer> l2 = add2(l); // ERROR!
}
}
The line marked as // ERROR! won't compile.
In the first method, add1, the compiler knows that it can assign the result to a variable of type ArrayList<Integer>, because the signature states that the return type of the method is exactly the same as that of the parameter. Since the parameter is of type ArrayList<Integer>, the compiler will infer T to be ArrayList<Integer>, which will allow you to assign the result to an ArrayList<Integer>.
In the second method, all the compiler knows is that it will return an instance of List<Integer>. It cannot be sure that it will be an ArrayList<Integer>, so you have to make an explicit cast, ArrayList<Integer> l2 = (ArrayList<Integer>) add2(l);. Note that this won't solve the problem: you are simply telling the compiler to stop whining and compile the code. You will still get an warning (unchecked cast), which can be silenced by annotating the method with #SuppressWarnings("unchecked"). Now the compiler will be quiet, but you might still get a ClassCastException at runtime!
The first one is specified to accept a List<Integer> and return a List<Integer>. List being an interface, the implication is that an instance of some concrete class that implements List is being passed as a parameter and an instance of some other concrete class that implements List is returned as a result, without any further relationship between these two classes other than that they both implement List.
The second one tightens that up: it is specified to accept some class that implements List<Integer> as a parameter, and return an instance of exactly that same class or a descendant class as the result.
So for example you could call the second one like so:
ArrayList list; // initialization etc not shown
ArrayList result = x.add2(list);
but not the first, unless you added a typecast.
What use that is is another question. ;-)
#Bruno Reis has explained the compile error.
And why does the second not compile, with the error shown below.
The error shown is actually reporting that you have tried to run code that failed to compile. It is a better idea to configure your IDE to not run code with compilation errors. Or if you insist on letting that happen, at least report the actual compilation error together with the line number, etc.
"I don't think I really understand Java generics."
Nobody does...
The issue is related to the interesting return type of getClass(). See its javadoc. And this recent thread.
In both of your examples, lst.getClass() returns Class<? extends List>, consequently, newInstance() returns ? extends List - or more formally, a new type parameter W introduced by javac where W extends List
In your first example, we need to assign W to List<Integer>. This is allowed by assignment conversion. First, W can be converted to List because List is a super type of W. Then since List is raw type, the optional unchecked conversion is allowed, which converts List to List<Integer>, with a mandatory compiler warning.
In the 2nd example, we need to assign W to T. We are out of luck here, there's no path to convert from W to T. It makes sense because as far as javac knows at this point, W and T could be two unrelated subclass of List.
Of course, we know W is T, the assignment would have been safe if allowed. The root problem here, is that getClass() loses type information. If x.getClass() returns Class<? extends X> without erasure, both of your examples will compile without even warning. They indeed are type safe.
Generics are a way to guarantee type safety.
Eg:
int[] arr = new int[4];
arr[0] = 4; //ok
arr[1] = 5; //ok
arr[2] = 9; //ok
arr[3] = "Hello world"; // you will get an exception saying incompatible
types.
By default arrays in Java are typeSafe. An integer array is only meant to
contain integer and nothing else.
Now:
ArrayList arr2 =new ArrayList();
arr2.add(4); //ok
arr2.add(5); //ok
arr2.(9); //ok
int a = arr2.get(0);
int b = arr2.get(1);
int c = arr3.get(2);
You willa gain get an exception like what it is not possible to cast Object
instance to integer.
The reason is that ArrayList stores object and not primitive like the
above array.
The correct way would be to explicitly cast to an integer.You have to do this
because type safety is not yet guaranteed.
eg:
int a = (int)arr2.get(0);
To employ type safety for collections, you simply specify the type of objects that your collection contains.
eg:
ArrayList<Integer> a = new ArrayList<Integer>();
After insertion into the data structure, you can simply retrieve it like you
would do with an array.
eg:
int a = arr2.get(0);
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.