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.
Related
Here's my sample code:
public class MyList<T extends Number> {
private List<T> items;
public void func() {
items.add(Integer.valueOf(1));
}
}
I think I should be able to add integer to items, but compilation fails:
Required type: T
Provided: Integer
Anyone knows what's wrong here?
Let us consider a more complete version of your example:
public class MyList<T extends Number> {
private List<T> items = new ArrayList<>();
public void func() {
items.add(Integer.valueOf(1));
}
}
Suppose for the sake of argument that the compiler says that is OK.
And now we will create an instance and call the func method:
MyList<Double> myDoubles = new MyList<>();
myDoubles.func();
Here is what happens.
We create a MyList instance where T is Double. That's OK: the Double class implements the Number interface.
The items has a notional type of List<Double> and we initialize it with an ArrayList. So we now have a list what should only contain Double values.
In the call to func we attempt to add an Integer to the List<Double>. That is wrong!
That is what the compilation error is saying with the Required type: T Provided: Integer message.
To spell it out, the compiler expects a value whose type is the type that T is going to be at runtime. But you have given it Integer. While Integer implements the Number interface, it is not necessary the same as what T will be at runtime. That is the root cause of your compilation error.
So what is the solution?
Well it depends on what the (actual) problem that this example is intended to solve. If you want item to be able to hold any Number, you should change
private List<T> items = new ArrayList<>();
to
private List<Number> items = new ArrayList<>();
and items.add(Integer.valueOf(1)) should work.
On the other hand, if you want to add 1 to items as an instance of the runtime type of T, that is much more difficult. The problem is that the code of MyList (as written) does not and cannot know what that type is! So, you need to EITHER pass the T instance representing 1 as a parameter to func OR pass a Class<T> parameter to func or the constructor and use reflection to create the instance of that class to represent 1.
But if you want something to auto-magically convert the Integer to what ever the actual runtime type of T is ... that is not possible.
When you're using a so-called bounded type parameter <T extends Number> means that type T is restricted by an upper bound expected to be a Number or one of its subtypes.
There are plenty of options if you would think about subtypes of Number: BigDecimal, AtomicLong, etc. That mean that your List<T> at runtime might appear to be a List<AtomicLong> and since behavior of generic types is invariant we would not be able to add anything that is not of type AtomicLong into such list (no Strings, no Integers, etc.).
Therefore, compiler would disallow to add an Integer into a List<T>, where T can be anything that extends Number (or the Number itself), because it can't be sure that it's type-safe.
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 am reading on Generics in Java atm, and things go a little slow would love some help. I read from Oracles own database:
https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
At the bottom we can see List<Integer> is a subtype of List<? extends Number>
I also came across this stackoverflow question:
Java Generic type : difference between List <? extends Number> and List <T extends Number>
Which says in one answer: this is true:
((List<Integer>)list).add((int) s);
I have verified it, so that's ok. But I don't understand It completely.
What if the Wildcard is the Short class and I add a number higher than 2^15-1(=32767) Shouldn't it give error?
I even tried something like this and it works fine:
import java.util.*;
class CastingWildcard{
public static void main(String[] args){
List<? extends Number> list = new ArrayList<Short>();
int s=32770;
((List<Integer>)list).add((int) s);
System.out.println(list.get(0));
}
}
To sum up: Why Can I cast List<? extends Number> to List<Integer> when the wildcard could be Short, and even Byte, which also extends Number?
The cast makes the compiler ignore the fact, that the types may not be assignable.
At runtime the type parameters are unimportant, see type erasure.
The ArrayList internally stores the content in a Object[] array, which means you can add any reference type to the list object, if you "abuse" casting.
You may get a exception when you retrieve a Object though, since there's a cast hidden in the get statement.
Example:
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
List<String> list2 = (List) list;
list2.add("Hello World");
Integer i = list.get(0); // works
String s = list2.get(3); // works
s = list2.get(1); // ClassCastException
i = list.get(3); // ClassCastException
You can cast an object to anything you want, but it might fail at runtime. However since generics information isn't present during runtime, your code becomes essentially ((List)list).add(s);. At that point list will take any object, not just a Number. Generics can help you avoid casts and keep type safety during compile time, but during runtime they don't matter anymore.
This question already has answers here:
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
(19 answers)
Closed 6 years ago.
Consider the snippet:
Number[] numbers = {1, 2.3, 4.5f, 6000000000000000000L};
It's perfectly okay to do the above, Number is an abstract class.
Going ahead,
List<Long> listLong = new ArrayList<Long>();
listLong.add(Long.valueOf(10));
List<Number> listNumbers = listLong; // compiler error - LINE 3
listNumbers.add(Double.valueOf(1.23));
Had Line 3 was designed to be compiled successfully,
we would end up with a List of Numbers, i.e,
for(Number num: listNumbers ){
System.out.println(num);
}
// 10
// 1.23
which are all numbers.
I came across this in a book,
Generics doesn’t support sub-typing because it will cause issues in
achieving type safety. That’s why List<T> is not considered as a
subtype of List<S> where S is the super-type of T
Which type safety would have lost in this specific case as discussed above, were the Line 3 was to be compile successfully?
List<Long> listLong = new ArrayList<Long>();
List<Number> listNumbers = listLong;
So, listNumbers and listLong would be two references to the same list, if that was possible, right?
listNumbers.add(Double.valueOf(1.23));
So, you would be able to add a Double to that list. listLong, of type List<Long>, would thus contain a Double. The type-safety would thus be broken.
If that was the case, then we could add other different subtypes of Number into listNumbers, which must be forbidden.
Imagine you're now inserting objects of type Double and Long, and later you try to use Long#reverse. Your code will compile but of course will fail at runtime (bad) the first Double it'll come through.
Let's use an example with a non-abstract base class:
public class Human {
public string getName() {
// ...
}
}
public class Student extends Human {
public void learn(Subject subject) {
// ...
}
}
public class Teacher extends Human {
public void teach(Subject subject) {
// ...
}
}
At any place where a Human is expected, a Student or Teacher will do just as well, as they fully implement the Human interface. (In this case, that getName() can be called on them.) Java inheritance guarantees that this is the case technically. Making it work semantically is the class author's job, so that his code fulfils the Liskov substitution principle.
So doesn't this mean that we can also substitute Collection<Teacher> where a Collection<Human> is expected? Not always. Consider the following method:
public class Human {
// ...
public void join(Set<Human> party) {
party.add(this);
}
}
Now, if Java allowed a Set<Student> to be passed as party, any attempts of non-Student Humans to join that party would have to fail at runtime.
As a general rule, a container of a subtype is unsuitable if the receiver (callee in case of a function argument, caller in case of a function return value) wants to put something into it, but acceptable if the receiver only want to take stuff out and use it. A container of a supertype is unsuitable if the receiver wants to take stuff out and use it, but acceptable if the receiver only ever puts stuff into it. As a result, if the receiver both takes stuff out of the collection and puts stuff into the collection, they usually must require a collection of a fixed type.
Our join method only puts Humans into the party, so we could also allow a Set<Object> or a non-generic Set or equivalently a Set<?>. Java allows us to do that with lower-bounded wildcards:
public class Human {
// ...
public void join(Set<? super Human> party) {
party.add(this);
}
}
For opening up the possibilities towards subclasses, there's upper-bounded wildcards:
public class Teacher extends Human {
public void teach(Subject subject, Set<? extends Student> schoolClass) {
for (Student student : class) {
student.learn(subject);
}
}
}
Now, if we ever subclass Student, the passed schoolClass can be a Set of that subtype, too.
The concept you are referring to is variance.
In other words, if S is a supertype of T, is List<S> a subtype, supertype, equal type, or unreleted to List<T>?
The answer for List -- and all other Java generics* -- is "unrelated", i.e. invariant.
class SuperType {}
class Type extends SuperType {}
class SubType extends Type {}
List<Type> list = ...
List<SuperType> superList = list;
superList.add(new SuperType());
// no, we shouldn't be able to add a SuperType to list
List<SubType> subList = list;
SubType item = subList.get(0);
// no, there's not necessarily only SubType items in list
*Java does have the notion of "use-site" variance, with wildcards (?). This will limit what methods are possible to call.
List<Type> list = ...
List<? super SubType> wildcardList = list;
wildcardList.add(new SubType());
// but...everything we get() is an Object
or
List<Type> list = ...
List<? extends SuperType> wildcardList = list;
SuperType item = wildcard.get(0);
// but...it's impossible to add()
FYI, some languages have the notion of definition-site variance, e.g. Scala. So List[Int] is indeed a subtype of List[Number]. That's possible with immutable collections (again, a limited set of methods), but obviously not for mutable ones.
I have the following code:
public <T extends SomeObject> long doSomething(T someObject){
List<? extends SomeObject> l = new LinkedList<>();
l.add(someObject);
}
this causes a compilation error - telling me that there is no suitable methods found: add(T),
why is that?
If l accept things that extends SomeObject shouldn't it accept someObject as it bounds to extend SomeObject?
List<? extends SomeObject> l
What do you mean by that? Of course it will generate an error.
Take this example :SomeObject is Fruit, you have 2 derived classes Apple and Orange
Your list what will it contain? Apples or Oranges? The compiler cannot tell. So it generates error.
If you replace List<? extends SomeObject> l with List<SomeObject> l. Then this will work because Apple and Orange are both Fruit.
I would advise you to use this statement:
List<T> l = new LinkedList<T>();
This is no less type-safe then
List<SomeObject> l = new LinkedList<SomeObject>();
and additionally gives you an opportunity to get objects of type T from the list without casting. T is already SomeObject so no casting required to call methods of SomeObject on T.
And all that with less typing!
Back to the problem.
First thing to note is that wildcard type "?" means unknown, this is important.
You may, however, specify an upper (? extends) or a lower (? super) constraint to it.
You declared a list as "List".
List is known to have objects of SomeObject inside. but! the exact type of objects is unknown.
Compiler can not say if there are instances of "class A extends SomeObject" or instances of "class B extends SomeObject" inside the list.
If you call list.get() it can only say that there will be an object of type SomeObject.
SomeObject obj = list.get(1); // Ok
But inserting an object of any(!) type is unsafe because the actual type of elements in the list is unknown.
You could wonder why wildcard type ever exists.
It is here to lower restriction in type casting that will be too strict otherwise.
Sample
class A { }
class A2 extends A { }
class B <T> {
void change(T a) { .. };
T read() { .. };
}
If there were no wildcards we would not be able to do this: B<A> b = new B<A2>(); - it does not work.
This is because type conversion from B<A> to B<A2> is unsafe.
Why? Let's look (copied from http://en.wikipedia.org/wiki/Generics_in_Java)
List<Integer> ints = new ArrayList<Integer>();
ints.add(2);
List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number>
nums.add(3.14);
Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
What is the solution? Sometimes, we want to do such assignments or pass parameters in a general way!
Wildcard type helps here: B<? extends A> b = new B<A2>();
Method B.void change(T a) is now disabled - this is what your question was about and explained in the first part.
Method B.T read() is still valid and returns A: A a = b.read();. Yes, it returns A2 actually but to the caller of b.read() it's visible as A.
Wildcard types are widely used in Collections Framework.