So, to cite the textbook example, say you are implementing a Stack<E>. It will probably have the methods:
public void push(E element);
public E pop();
and, for convenience: public void pushAll(Iterable<? extends E> elements);
public void popAll(Collection<? super E> destination);
The pushAll has "? extends" because the elements Iterable will "produce" elements for the stack. The popAll has super because the destination Collection will "consume" elements from the stack. It is not that hard, is it? Let's note that guard-of-terra is talking about proficiency, not mere familiarity. I believe reading "Effective Java" is a nice way to get closer to the proficient level.Let it be noted that this whole mess exists because generics in Java were implemented with type erasure so their introduction wouldn't break legacy code. I personally think this was a bad idea, but it does show that when a language is evolving, there are a bunch of constraints the designers must be aware of.
[1] http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp...