В Java ковариация позволяет разработчику API указать, что экземпляр может быть обобщен как определенный тип или любой из подтипов этого типа. Например:
List<? extends Shape> shapes = new ArrayList<Circle>();
// where type Circle extends Shape
Контравариантность идет другим путем. Это позволяет нам указать, что экземпляр может быть обобщен как определенный тип или супертип.
List<? super Shape> shapes = new ArrayList<Geometry>();
// where Shape extends Geometry
Чем полезна контравариантность универсального Java-шаблона? Когда бы вы выбрали его использование?