Как исправить эту ошибку подстановочного знака Java?
-
05-07-2019 - |
Вопрос
В этом вопрос, у TofuBeer возникли проблемы с созданием обобщенного IterableEnumeration
.
Ответ пришел от jcrossley3, указав на эту ссылку http://www.javaspecialists.eu/archive/Issue107.html что в значительной степени решило проблему.
Есть еще одна вещь, которую я не понимаю.Настоящая проблема, как удачно отметил Эриксон, заключалась в следующем:
Вы не можете указать подстановочный знак при создании параметризованного типа.
Но удаление подстановочного знака в объявлении тоже не сработало:
final IterableEnumeration<ZipEntry> iteratable
= new IterableEnumeration<ZipEntry>(zipFile.entries());
Результат: следующая ошибка:
Main.java:19: cannot find symbol
symbol : constructor IterableEnumeration(java.util.Enumeration<capture#469 of ? extends java.util.zip.ZipEntry>)
location: class IterableEnumeration<java.util.zip.ZipEntry>
final IterableEnumeration<ZipEntry> iteratable = new IterableEnumeration<ZipEntry>( zipFile.entries());
^
1 error
Но примеры в JavaSpecialist работают:
IterableEnumeration<String> ie =
new IterableEnumeration<String>(sv.elements());
Единственное отличие, которое я могу заметить, это то, что в блоге JavaSpecialists Enumeration
происходит от Vector
чья подпись:
public Enumeration<E> elements()
в то время как тот, который терпит неудачу, исходит от ZipFile
чья подпись:
public Enumeration<? extends ZipEntry> entries()
Наконец, все это поглощается конструкцией for-each и статическим методом make, предложенным в ссылке.
for(final ZipEntry entry : IterableEnumeration.make( zipFile.entries() )) {
if(!(entry.isDirectory())) {
names.add(entry.getName());
}
}
Но!!Цель этого информационного бюллетеня заключалась не в том, чтобы решить эту проблему, а в том, чтобы избежать необходимости указывать общий тип только потому, что синтаксис выглядит уродливо!
Так..мои вопросы:
Что происходит?
Почему не создается экземпляр IterableEnumeration
работать, когда параметр является Enumeration
чей тип <? extends SomeClass>
?И почему конструкция make for-each поглощает проблему?!!!
Почему это работает:
for(final ZipEntry entry : IterableEnumeration.make( zipFile.entries() )) {
но это не работает?
final IterableEnumeration<ZipEntry> iteratable
= IterableEnumeration.make( zipFile.entries() );
Ниже приведена (слегка) измененная версия исходного кода TofuBeer:
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.Vector;
public class Main {
private ZipFile zipFile;
public Set<String> entries() {
final Vector<ZipEntry> vector = new Vector<ZipEntry>();
// why this works.
//final IterableEnumeration<ZipEntry> iteratable = new IterableEnumeration<ZipEntry>( vector.elements() );
// but this do not.
//final IterableEnumeration<ZipEntry> iteratable = new IterableEnumeration<ZipEntry>( zipFile.entries() );
// nor this
final IterableEnumeration<ZipEntry> iteratable = IterableEnumeration.make( zipFile.entries() );
// And what's with the for-each that doesn't care about the type?
final Set<String> names = new HashSet<String>();
for(final ZipEntry entry : IterableEnumeration.make( zipFile.entries() )) {
if(!(entry.isDirectory())) {
names.add(entry.getName());
}
}
return (names);
}
}
class IterableEnumeration<T> implements Iterable<T> {
private final Enumeration<T> enumeration;
public IterableEnumeration(final Enumeration<T> e) {
enumeration = e;
}
public Iterator<T> iterator() {
return new Iterator<T>() {
public boolean hasNext() {
return (enumeration.hasMoreElements());
}
public T next() {
return (enumeration.nextElement());
}
public void remove() {
throw new UnsupportedOperationException("Cannot remove via an Enumeration");
}
};
}
// As suggested by http://www.javaspecialists.eu/archive/Issue107.html
// but doesn't help with: final IterableEnumeration<ZipEntry> iteratable = IterableEnumeration.make( zipFile.entries() );
public static <T> Iterable<T> make(Enumeration<T> en) {
return new IterableEnumeration<T>(en);
}
}
Я хочу это понять!!
Решение
Я не уверен, что происходит с циклом foreach, но вам нужно добавить подстановочный знак в объявление IterableEnumeration, чтобы принять неуказанный тип, возвращаемый из ZipFile.entries().
Заменять
private final Enumeration<T> enumeration;
public IterableEnumeration(final Enumeration<T> e) {
enumeration = e;
}
public static <T> Iterable<T> make(Enumeration<T> en) {
return new IterableEnumeration<T>(en);
}
С
private final Enumeration<? extends T> enumeration;
public IterableEnumeration(final Enumeration<? extends T> e) {
enumeration = e;
}
public static <T> Iterable<T> make(Enumeration<? extends T> en) {
return new IterableEnumeration<T>(en);
}
Другие советы
Основная проблема здесь в том, что когда Zip-файл был изменен для поддержки дженериков, сопровождающий решил сделать возвращаемый тип entries()
методы Enumeration<? extends ZipEntry>
(предположительно для того, чтобы метод в подклассе JarFile
могу вернуться Enumeration<JarEntry>
).Это вызывает проблему, которую вы видите.
Потому что Enumeration<T>
используется ковариантно (как всегда - он возвращает только значения), вы всегда должны заставлять аргументы метода принимать Enumeration<? extends T>
.