Как исправить эту ошибку подстановочного знака Java?

StackOverflow https://stackoverflow.com/questions/621117

Вопрос

В этом вопрос, у 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>.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top