Есть ли разница между перечислением‹? расширяет ZipEntry› и Enumeration‹ZipEntry›? Если да, то в чем разница?
Разница между перечислением‹? расширяет ZipEntry› и Enumeration‹ZipEntry›?
Ответы (3)
Нет никакой практической разницы в том, что вы можете сделать, когда у вас есть один из них, потому что параметр типа используется только в «выходной» позиции. С другой стороны, есть большая разница в том, что вы можете использовать в качестве одного из них.
Предположим, у вас есть Enumeration<JarEntry> — вы не можете передать его методу, который принимает Enumeration<ZipEntry> в качестве одного из своих аргументов. Однако вы можете передать его методу, принимающему Enumeration<? extends ZipEntry>.
Более интересно, когда у вас есть тип, который использует параметр типа как во входных, так и в выходных позициях — List<T> является наиболее очевидным примером. Вот три примера методов с вариациями параметра. В каждом случае мы попытаемся получить элемент из списка и добавить еще один.
// Very strict - only a genuine List<T> will do
public void Foo(List<T> list)
{
T element = list.get(0); // Valid
list.add(element); // Valid
}
// Lax in one way: allows any List that's a List of a type
// derived from T.
public void Foo(List<? extends T> list)
{
T element = list.get(0); // Valid
// Invalid - this could be a list of a different type.
// We don't want to add an Object to a List<String>
list.add(element);
}
// Lax in the other way: allows any List that's a List of a type
// upwards in T's inheritance hierarchy
public void Foo(List<? super T> list)
{
// Invalid - we could be asking a List<Object> for a String.
T element = list.get(0);
// Valid (assuming we get the element from somewhere)
// the list must accept a new element of type T
list.add(element);
}
Для получения более подробной информации прочитайте:
- Руководство по универсальным шаблонам языка Java
- Учебное пособие по дженерикам Java (PDF)
- Часто задаваемые вопросы о дженериках Java, в частности раздел о подстановочных знаках
Да, прямо из одного из руководств по универсальным средствам Sun:
Здесь Shape — это абстрактный класс с тремя подклассами: Circle, Rectangle и Triangle.
public void draw(List<Shape> shape) { for(Shape s: shape) { s.draw(this); } }Стоит отметить, что метод draw() можно вызывать только для списков Shape и нельзя вызывать, например, для списков Circle, Rectangle и Triangle. Чтобы метод принимал любую форму, он должен быть записан следующим образом:
public void draw(List<? extends Shape> shape) { // rest of the code is the same }
Вы только что ушли и напомнили мне о чем-то, что мне хотелось бы иметь в мире C#.
Помимо предоставленных ссылок, в ответах на этот вопрос есть несколько хороших ссылок о C# и Java, связанных с этой темой: Logic and its application to Collections .Generic и наследование
Выбор из которых:
- Контравариантность и Ковариация в C# (код специфичен для C# и является теоретическим, поскольку синтаксис [пока] не существует, но в целом очень хорошо охватывает темы.)
- Общие шаблоны Java, ковариантность и контравариантность аргументов
- руководство по дженерикам Java от Sun.