import java.util.List;
import java.util.ArrayList;
interface Canine {}
class Dog implements Canine {}
public class Collie extends Dog {
public static void main(String[] args){
List<Dog> d = new ArrayList<Dog>();
List<Collie> c = new ArrayList<Collie>();
d.add(new Collie());
c.add(new Collie());
do1(d); do1(c);
do2(d); do2(c);
}
static void do1(List<? extends Dog> d2){
d2.add(new Collie());
System.out.print(d2.size());
}
static void do2(List<? super Collie> c2){
c2.add(new Collie());
System.out.print(c2.size());
}
}
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
What does it mean 'when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both' ?
As far as I know,
The method do1 has List<? extends Dog> d2 so d2 only can be accessed but not modified.
The method d2 has List<? super Collie> c2 so c2 can be accessed and modified and there is no compilation error.
Generic guidelines
You cannot add a Cat to a List<? extends Animal> because you don't know what kind of list that is. That could be a List<Dog> also. So you don't want to throw your Cat into a Black Hole. That is why modification of List declared that way is not allowed.
Similarly when you fetch something out of a List<? super Animal>, you don't know what you will get out of it. You can even get an Object, or an Animal. But, you can add an Animal safely in this List.
I pasted your code into my IDE. The following error was signalled inside do1:
The method add(capture#1-of ? extends Dog) in the type List is not applicable for the arguments (Collie)
This is, of course, as expected.
You simply cannot add a Collie to a List<? extends Dog> because this reference may hold for example a List<Spaniel>.
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
That's a fair first approximation, but not quite correct. More correct would be:
You can only add null to a Collection<? extends Dog> because its add method takes an argument of ? extends Dog. Whenever you invoke a method, you must pass parameters that are of a subtype of the declared parameter type; but for the parameter type ? extends Dog, the compiler can only be sure that the argument is of compatible type if the expression is null. However, you can of course modify the collection by calling clear() or remove(Object).
On the other hand, if you read from a Collection<? super Dog>, its iterator has return type ? super Dog. That is, it will return objects that are a subtype of some unknown supertype of Dog. But differently, the Collection could be a Collection<Object> containing only instances of String. Therefore
for (Dog d : collection) { ... } // does not compile
so the only thing we know is that instances of Object are returned, i.e. the only type-correct way of iterating such a Collection is
for (Object o : collection) { ... }
but it is possible to read from a collection, you just don't know what types of objects you will get.
We can easily generalize that observation to: Given
class G<T> { ... }
and
G<? extends Something> g;
we can only pass null to method parameters with declared type T, but we can invoke methods with return type T, and assign the result a variable of type Something.
On the other hand, for
G<? super Something> g;
we can pass any expression of type Something to method parameters with declared type T, and we can invoke methods with return type T, but only assign the result to a variable of type Object.
To summarize, the restrictions on the use of wildcard types only depend on the form of the method declarations, not on what the methods do.
I pasted your code into IDEONE http://ideone.com/msMcQ. It did not compile for me - which is what I expected. Are you sure you did not have any compilation errors?
Related
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 code in my project that looks like this:
public interface Bar<T extends Foo<?>> {
//...
}
public class MyFoo implements Foo<String> {
private List<Bar<Foo<String>> barFoo = ...
public <U extends Foo<String>> boolean addBar(Bar<? extends U> b) {
barFoo.add((Bar<Foo<String>>) b); //safe cast?
}
}
Eclipse gives a warning for the cast in addBar that the cast is unsafe. However, am I correct in assuming that the cast will not throw given the restrictions that I have put on the type parameters, and therefore the cast is indeed safe?
Not in general.
Suppose Bar has a method void get(T value), and there are two implementations of Foo<String>, MyFoo and YourFoo. Now suppose a caller calls addBar on a value of type Bar<MyFoo>. This works: when U = Foo<String>, we have that Bar<MyFoo> is a subtype of Bar<? extends U>. Now we cast that value to a Bar<Foo<String>>.
Now if Bar has no methods that accept T's as arguments, there's no problem. But suppose it has a method void process(T value). The implementation we called has T = MyFoo, so it only has a process(MyFoo value) method. Once we cast it to a Bar<Foo<String>>, though, we might call it with a YourFoo instead. This is illegal.
Stab in the dark, but I suspect that what you really wanted to do was declare barFoo as a List<? extends Bar<? extends Foo<String>>.
This is not a safe cast. Eclipse is correct.
Imagine you has a class MyFoo that extends Foo and you passed in a Bar<MyFoo<String>> Now some method in Bar with a myMethod(Foo x) signature when only a myMethod(MyFoo x) signature was compiled, so the method lookup would fail.
The cast is not safe, because although U extends Foo<String>, it is not (necessarily) the case that Bar<U> extends Bar<Foo<String>>. In fact, Bar<U> will only extend Bar<Foo<String>> when they are the same thing, i.e., when U is Foo<String>.
Intuitively, it may seem that (for example) List<String> should be a subtype of List<Object>, but this is not how generics work. List<String> is a subtype of List<? extends Object>, but it is not a subtype of List<Object>. (It may make more sense to consider an example like Comparable<T>: Comparable<String> means "can be compared to any String, whereas Comparable<Object> means "can be compared to any Object". It should be clear that Comparable<String> should not be a subtype of Comparable<Object>.)
[…] the cast will not throw […], and therefore the cast is indeed safe?
I think you're misunderstanding the nature of the warning. Eclipse is warning you that this cast will not throw even when it should, and this is actually why it's not safe. For example, this code:
final Object o = Integer.valueOf(7);
final String s = (String) o;
is perfectly safe, because the cast will throw an exception. But this code:
final List<?> wildcardList = new ArrayList<Integer>(Integer.valueOf(7));
final List<String> stringList = (List<String>) wildcardList;
is unsafe, because the runtime has no way of checking the cast (due to erasure), so it won't throw an exception, even though it's wrong: stringList is now a List<String> whose first element is of type Integer. (What happens is, at some point later on, you can get a spontaneous ClassCastException when you try to do something with that element.)
Below code :
List<? extends String> genericNames = new ArrayList<String>();
genericNames.add("John");
Gives compiler error :
Multiple markers at this line
- The method add(capture#1-of ? extends String) in the type List is not applicable for the
arguments (String)
- The method add(capture#1-of ?) in the type List is not applicable for the arguments (String)
What is causing this error ? Should I not be able to add Strings or its subtype since I am extending String within the type parameter ?
When you use wildcards with extends, you can't add anything in the collection except null. Also, String is a final class; nothing can extend String.
Reason: If it were allowed, you could just be adding the wrong type into the collection.
Example:
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
Now you have List<? extends Animal>
public static void someMethod(List<? extends Animal> list){
list.add(new Dog()); //not valid
}
and you invoke the method like this:
List<Cat> catList = new ArrayList<Cat>();
someMethod(catList);
If it were allowed to add in the collection when using wildcards with extends, you just added a Dog into a collection which accepts only Cat or subtype type. Thus you can't add anything into the collection which uses wildcards with upper bounds.
String is a final class and cannot be extended. Additionally, for the case you seem to be interested in, you do not need the extends keyword. List<String> will do what you seem to want. That will allow Strings and sub-classes of String (if such a thing could exist, which it can't since String is final).
Just want to add to the answer of GanGnaMStYleOverFlow that you can add an object of any subtype of Animal to the following list:
List<Animal> animals = new ArrayList<Animal>();
You should use such list whenever you think that it can contain any kind of animals.
On the other hand, you should use List<? extends Animal> when you want to specify that the list contains some kind of animal but you don't know which one. Since you don't know what kind of animals are there, you cannot add any.
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.
Why can I use super only with wildcards and not with type parameters?
For example, in the Collection interface, why is the toArray method not written like this
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
super to bound a named type parameter (e.g. <S super T>) as opposed to a wildcard (e.g. <? super T>) is ILLEGAL simply because even if it's allowed, it wouldn't do what you'd hoped it would do, because since Object is the ultimate super of all reference types, and everything is an Object, in effect there is no bound.
In your specific example, since any array of reference type is an Object[] (by Java array covariance), it can therefore be used as an argument to <S super T> S[] toArray(S[] a) (if such bound is legal) at compile-time, and it wouldn't prevent ArrayStoreException at run-time.
What you're trying to propose is that given:
List<Integer> integerList;
and given this hypothetical super bound on toArray:
<S super T> S[] toArray(S[] a) // hypothetical! currently illegal in Java
the compiler should only allow the following to compile:
integerList.toArray(new Integer[0]) // works fine!
integerList.toArray(new Number[0]) // works fine!
integerList.toArray(new Object[0]) // works fine!
and no other array type arguments (since Integer only has those 3 types as super). That is, you're trying to prevent this from compiling:
integerList.toArray(new String[0]) // trying to prevent this from compiling
because, by your argument, String is not a super of Integer. However, Object is a super of Integer, and a String[] is an Object[], so the compiler still would let the above compile, even if hypothetically you can do <S super T>!
So the following would still compile (just as the way they are right now), and ArrayStoreException at run-time could not be prevented by any compile-time checking using generic type bounds:
integerList.toArray(new String[0]) // compiles fine!
// throws ArrayStoreException at run-time
Generics and arrays don't mix, and this is one of the many places where it shows.
A non-array example
Again, let's say that you have this generic method declaration:
<T super Integer> void add(T number) // hypothetical! currently illegal in Java
And you have these variable declarations:
Integer anInteger
Number aNumber
Object anObject
String aString
Your intention with <T super Integer> (if it's legal) is that it should allow add(anInteger), and add(aNumber), and of course add(anObject), but NOT add(aString). Well, String is an Object, so add(aString) would still compile anyway.
See also
Java Tutorials/Generics
Subtyping
More fun with wildcards
Related questions
On generics typing rules:
Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
java generics (not) covariance
What is a raw type and why shouldn’t we use it?
Explains how raw type List is different from List<Object> which is different from a List<?>
On using super and extends:
Java Generics: What is PECS?
From Effective Java 2nd Edition: "producer extends consumer super"
What is the difference between super and extends in Java Generics
What is the difference between <E extends Number> and <Number>?
How can I add to List<? extends Number> data structures? (YOU CAN'T!)
As no one has provided a satisfactory answer, the correct answer seems to be "for no good reason".
polygenelubricants provided a good overview of bad things happening with the java array covariance, which is a terrible feature by itself. Consider the following code fragment:
String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;
This obviously wrong code compiles without resorting to any "super" construct, so array covariance should not be used as an argument.
Now, here I have a perfectly valid example of code requiring super in the named type parameter:
class Nullable<A> {
private A value;
// Does not compile!!
public <B super A> B withDefault(B defaultValue) {
return value == null ? defaultValue : value;
}
}
Potentially supporting some nice usage:
Nullable<Integer> intOrNull = ...;
Integer i = intOrNull.withDefault(8);
Number n = intOrNull.withDefault(3.5);
Object o = intOrNull.withDefault("What's so bad about a String here?");
The latter code fragment does not compile if I remove the B altogether, so B is indeed needed.
Note that the feature I'm trying to implement is easily obtained if I invert the order of type parameter declarations, thus changing the super constraint to extends. However, this is only possible if I rewrite the method as a static one:
// This one actually works and I use it.
public static <B, A extends B> B withDefault(Nullable<A> nullable, B defaultValue) { ... }
The point is that this Java language restriction is indeed restricting some otherwise possible useful features and may require ugly workarounds. I wonder what would happen if we needed withDefault to be virtual.
Now, to correlate with what polygenelubricants said, we use B here not to restrict the type of object passed as defaultValue (see the String used in the example), but rather to restrict the caller expectations about the object we return. As a simple rule, you use extends with the types you demand and super with the types you provide.
The "official" answer to your question can be found in a Sun/Oracle bug report.
BT2:EVALUATION
See
http://lampwww.epfl.ch/~odersky/ftp/local-ti.ps
particularly section 3 and the last paragraph on page 9. Admitting
type variables on both sides of subtype constraints can result in a
set of type equations with no single best solution; consequently,
type inference cannot be done using any of the existing standard
algorithms. That is why type variables have only "extends" bounds.
Wildcards, on the other hand, do not have to be inferred, so there
is no need for this constraint.
####.### 2004-05-25
Yes; the key point is that wildcards, even when captured, are only used
as inputs of the inference process; nothing with (only) a lower bound needs
to be inferred as a result.
####.### 2004-05-26
I see the problem. But I do not see how it is different from the problems
we have with lower bounds on wildcards during inference, e.g.:
List<? super Number> s;
boolean b;
...
s = b ? s : s;
Currently, we infer List<X> where X extends Object as the type of the
conditional expression, meaning that the assignment is illegal.
####.### 2004-05-26
Sadly, the conversation ends there. The paper to which the (now dead) link used to point is Inferred Type Instantiation for GJ. From glancing at the last page, it boils down to: If lower bounds are admitted, type inference may yield multiple solutions, none of which is principal.
The only reason is it makes no sense when declaring a type parameter with a super keyword when defining at a class level.
The only logical type-erasure strategy for Java would have been to fallback to the supertype of all objects, which is the Object class.
A great example and explanation can be found here:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#Why%20is%20there%20no%20lower%20bound%20for%20type%20parameters?
A simple example for rules of type-erasure can be found here:
https://www.tutorialspoint.com/java_generics/java_generics_type_erasure.htm#:~:text=Type%20erasure%20is%20a%20process,there%20is%20no%20runtime%20overhead.
Suppose we have:
basic classes A > B > C and D
class A{
void methodA(){}
};
class B extends A{
void methodB(){}
}
class C extends B{
void methodC(){}
}
class D {
void methodD(){}
}
job wrapper classes
interface Job<T> {
void exec(T t);
}
class JobOnA implements Job<A>{
#Override
public void exec(A a) {
a.methodA();
}
}
class JobOnB implements Job<B>{
#Override
public void exec(B b) {
b.methodB();
}
}
class JobOnC implements Job<C>{
#Override
public void exec(C c) {
c.methodC();
}
}
class JobOnD implements Job<D>{
#Override
public void exec(D d) {
d.methodD();
}
}
and one manager class with 4 different approaches to execute job on object
class Manager<T>{
final T t;
Manager(T t){
this.t=t;
}
public void execute1(Job<T> job){
job.exec(t);
}
public <U> void execute2(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
public <U extends T> void execute3(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
//desired feature, not compiled for now
public <U super T> void execute4(Job<U> job){
U u= (U) t; //safe
job.exec(u);
}
}
with usage
void usage(){
B b = new B();
Manager<B> managerB = new Manager<>(b);
//TOO STRICT
managerB.execute1(new JobOnA());
managerB.execute1(new JobOnB()); //compiled
managerB.execute1(new JobOnC());
managerB.execute1(new JobOnD());
//TOO MUCH FREEDOM
managerB.execute2(new JobOnA()); //compiled
managerB.execute2(new JobOnB()); //compiled
managerB.execute2(new JobOnC()); //compiled !!
managerB.execute2(new JobOnD()); //compiled !!
//NOT ADEQUATE RESTRICTIONS
managerB.execute3(new JobOnA());
managerB.execute3(new JobOnB()); //compiled
managerB.execute3(new JobOnC()); //compiled !!
managerB.execute3(new JobOnD());
//SHOULD BE
managerB.execute4(new JobOnA()); //compiled
managerB.execute4(new JobOnB()); //compiled
managerB.execute4(new JobOnC());
managerB.execute4(new JobOnD());
}
Any suggestions how to implement execute4 now ?
==========edited =======
public void execute4(Job<? super T> job){
job.exec( t);
}
Thanks to all :)
========== edited ==========
private <U> void execute2(Job<U> job){
U u= (U) t; //now it's safe
job.exec(u);
}
public void execute4(Job<? super T> job){
execute2(job);
}
much better, any code with U inside execute2
super type U becomes named !
interesting discussion :)
I really like the accepted answer, but I would like to put a slightly different perspective on it.
super is supported in a typed parameter only to allow contravariance capabilities. When it comes to covariance and contravariance it's important to understand that Java only supports use-site variance. Unlike Kotlin or Scala, which allow declaration-site variance. Kotlin documentation explains it very well here. Or if you're more into Scala, here's one for you.
It basically means that in Java, you can not limit the way you're gonna use your class when you declare it in terms of PECS. The class can both consume and produce, and some of its methods can do it at the same time, like toArray([]), by the way.
Now, the reason extends is allowed in classes and methods declarations is because it's more about polymorphism than it is about variance. And polymorphism is an intrinsic part of Java and OOP in general: If a method can accept some supertype, a subtype can always safely be passed to it. And if a method, at declaration site as it's "contract", should return some supertype, it's totally fine if it returns a subtype instead in its implementations