Explanation of the get-put principle
Consider a bunch of bananas. This is a Collection<? extends Fruit>
in that it's a collection of a particular kind of fruit - but you don't know (from that declaration) what kind of fruit it's a collection of. You can get an item from it and know it will definitely be a fruit, but you can't add to it - you might be trying to add an apple to a bunch of bananas, which would definitely be wrong. You can add null
to it, as that will be a valid value for any kind of fruit.
Now consider a fruitbowl. This is a Collection<? super Banana>
, in that it's a collection of some type "greater than" Banana
(for instance, Collection<Fruit>
or Collection<TropicalFruit>
). You can definitely add a banana to this, but if you fetch an item from the bowl you don't know what you'll get - it may well not be a banana. All you know for sure is that it will be a valid (possibly null
) Object
reference.
(In general, for Java generics questions, the Java Generics FAQ is an excellent resource which contains the answer to almost anything generics-related you're likely to throw at it.)
Get-put principles in Java generics
Nothing unexpected is happening here. Let's run through the steps:
List<? super A> list = new ArrayList<>();
We have a List
of ?
, which are above A
in the class hierarchy. Every ?
is an Object
, so we can think of it as a List<Object>
. Proceeding onwards...
list.add(new A());
System.out.println((list.get(0)
All good so far - list
has an A
inside, and it is fetched as an Object
.
.toString()));
We call toString
, which is invoked on an Object
(the A
). Dynamic dispatch proceeds to invoke the A
's toString
method (the lowest definition from the type hierarchy). However this is perfectly legal because toString
is defined for Object
as well as for A
. Moving on...
A a = list.get(0); //oops!
This breaks, as expected, because we try to convert an Object
from a List<Object>
into an A
, without a cast (e.g. A a = (A) list.get(0);
).
Overcoming generics put-get rule
No, it's absolutely fine to have two different kinds of Fruit
in a List<Fruit>
.
The issue comes when you've actually created a List<Banana>
. For example:
List<Banana> bananas = new ArrayList<>();
bananas.add(new Banana());
// This is fine!
List<? extends Fruit> fruits = bananas;
// Calling fruits.get(0) is fine, as it will return a Banana reference, which
// is compatible with a Fruit reference...
// This would *not* be fine
List<Fruit> badFruits = bananas;
badFruits.add(new Apple());
Banana banana = bananas.get(0); // Eek! It's an apple!
What is the difference between POST and PUT in HTTP?
Overall:
Both PUT and POST can be used for creating.
You have to ask, "what are you performing the action upon?", to distinguish what you should be using. Let's assume you're designing an API for asking questions. If you want to use POST, then you would do that to a list of questions. If you want to use PUT, then you would do that to a particular question.
Great, both can be used, so which one should I use in my RESTful design:
You do not need to support both PUT and POST.
Which you use is up to you. But just remember to use the right one depending on what object you are referencing in the request.
Some considerations:
- Do you name the URL objects you create explicitly, or let the server decide? If you name them then use PUT. If you let the server decide then use POST.
- PUT is defined to assume idempotency, so if you PUT an object twice, it should have no additional effect. This is a nice property, so I would use PUT when possible. Just make sure that the PUT-idempotency actually is implemented correctly in the server.
- You can update or create a resource with PUT with the same object URL
- With POST you can have 2 requests coming in at the same time making modifications to a URL, and they may update different parts of the object.
An example:
I wrote the following as part of another answer on SO regarding this:
POST:
Used to modify and update a resource
POST /questions/<existing_question> HTTP/1.1
Host: www.example.com/Note that the following is an error:
POST /questions/<new_question> HTTP/1.1
Host: www.example.com/If the URL is not yet created, you
should not be using POST to create it
while specifying the name. This should
result in a 'resource not found' error
because<new_question>
does not exist
yet. You should PUT the<new_question>
resource on the server first.You could though do something like
this to create a resources using POST:POST /questions HTTP/1.1
Host: www.example.com/Note that in this case the resource
name is not specified, the new objects
URL path would be returned to you.PUT:
Used to create a resource, or
overwrite it. While you specify the
resources new URL.For a new resource:
PUT /questions/<new_question> HTTP/1.1
Host: www.example.com/To overwrite an existing resource:
PUT /questions/<existing_question> HTTP/1.1
Host: www.example.com/
Additionally, and a bit more concisely, RFC 7231 Section 4.3.4 PUT states (emphasis added),
4.3.4. PUT
The PUT method requests that the state of the target resource be
created
orreplaced
with the state defined by the representation
enclosed in the request message payload.
What's the difference between these generic declarations?
- A
List
whose elements are lists of dogs - A
List
whose elements are lists of a type thatextends Dog
- A
List
whose elements are a single subtype of a list of dogs - A
List
whose elements are a single subtype of a (list of a type thatextends Dog
) - A
List
whose elements are a single supertype of a (list of a type thatextends Dog
) - A
List
whose elements are a single subtype of a (list of a type thatsuper Dog
)
Where "subtype", "supertype", "super", and "extends" are the "generic" versions (i.e. also includes the bounding type)
Examples with Number
and subtypes, because why not. Replace Dog
with Number
.
List<List<Number>>
might look like a 2D array ofNumber
elements. Pretty simple.List<List<? extends Number>>
might look like a 2D array, where each row is a different subtype of number. For example, the first row might be aList<Integer>
, the second row might be aList<Double>
, etc.List<? extends List<Number>>
might be aList<ArrayList<Number>>
,List<List<Number>>
,List<LinkedList<Number>>
, etc. TheNumber
has to stay, because generics are invariant, but you can haveList
or any of its subtypes as the "overall" type of the elements. You can only pick one ofList
or its subtypes though, and the one you pick you have to stay with.List<? extends List<? extends Number>>
is similar toList<? extends List<Number>>
, except now you can pickNumber
or any of its subclasses as the elements of the "overall" 2D array. So you can haveList<List<Integer>>
,List<ArrayList<Integer>>
,List<LinkedList<Double>>
, etc. As before, you can only pick one ofList
's subtypes and one ofNumber
's subtypes.- (tricky!)
List<? super List<? extends Number>>
appears to be equivalent toList<List<? extends Number>>
,List<Collection<? extends Number>>
, etc. but notList<List<Number>>
or anything concrete where a subtype ofNumber
is used. I think this is becauseList<Number>
isn't considered a supertype ofList<? extends Number>
, which I suppose makes sense due to generics being invariant.List<Object>
as well as raw types (List<List>
,List<Collection>
, etc.) also works. - Same thing as 4, except you get either
List<Number>
orList<Object>
(and apparentlyList<Serializable>
) as the inner list.
As @MadProgrammer said, due to PECS (Producer-Extends-Consumer-Super), any time you have a ? extends
for your generic type you won't be able to update the list, only retrieve items from it. So no add()
-ing and no set()
-ing.
Fundamentals on Generics
The problem basically is that by declaring v
as Vector<? extends SomeClass>
you say that it's a collection of SomeClass
or one of its subclasses. The compiler cannot make any assumptions about what specific type the container actually hold, even if the assignment to new Vector<SomeClass>()
is done on the same line. As much as the compiler knows v
might have been created as new Vector<SubClassOfSomeClass>()
. That is why it refuses to put SomeClass
instances (or anything else for that matter) to the collection. You can however get the SomeClass
elements out of it.
If you want to be able to put elements to the collection, then declare v
as Vector<? super SomeClass>
, by which you say that it's a container of SomeClass
or one of its superclasses. The compiler knows then that a SomeClass
instance will certainly fit in such a collection.
You cannot however get a SomeClass
element out of that collection, as the compiler knows nothing about the type of elements in it (except that it is some superclass of SomeClass
). By the definition of v
it might as well be just a collection of Object
s.
This is called the put/get principle in short which is discussed in detail also in other posts like this.
What is PECS (Producer Extends Consumer Super)?
tl;dr: "PECS" is from the collection's point of view. If you are only pulling items from a generic collection, it is a producer and you should use extends
; if you are only stuffing items in, it is a consumer and you should use super
. If you do both with the same collection, you shouldn't use either extends
or super
.
Suppose you have a method that takes as its parameter a collection of things, but you want it to be more flexible than just accepting a Collection<Thing>
.
Case 1: You want to go through the collection and do things with each item.
Then the list is a producer, so you should use a Collection<? extends Thing>
.
The reasoning is that a Collection<? extends Thing>
could hold any subtype of Thing
, and thus each element will behave as a Thing
when you perform your operation. (You actually cannot add anything (except null) to a Collection<? extends Thing>
, because you cannot know at runtime which specific subtype of Thing
the collection holds.)
Case 2: You want to add things to the collection.
Then the list is a consumer, so you should use a Collection<? super Thing>
.
The reasoning here is that unlike Collection<? extends Thing>
, Collection<? super Thing>
can always hold a Thing
no matter what the actual parameterized type is. Here you don't care what is already in the list as long as it will allow a Thing
to be added; this is what ? super Thing
guarantees.
Related Topics
What Is the Maximum Depth of the Java Call Stack
How to Execute a Java .Class from the Command Line
Hibernate 5 :- Org.Hibernate.Mappingexception: Unknown Entity
Java Stack Overflow Error - How to Increase the Stack Size in Eclipse
How to Count the Number of Matches for a Regex
How to Mark Jtable Cell Input as Invalid
Java Simpledateformat Always Returning January for Month
Calling One Jframe from Another Using Timer Without Any Buttons
How to Load Rsa Private Key from File
How to Interrupt a Serversocket Accept() Method
Should I Keep My Project Files Under Version Control
Getting the Filenames of All Files in a Folder
How to Properly Shutdown Java Executorservice
How to Find the Index of an Element in an Array in Java