Перечисления и общие методы в Java - PullRequest
3 голосов
/ 09 октября 2009

У меня все еще есть проблемы с некоторыми угловыми случаями в системе генериков java.

У меня есть этот метод (меня интересует только подпись):

 interface Extractor<RETURN_TYPE> {
    public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
}

(подумайте об интерфейсе, реализация которого иногда извлекает EnumSet, иногда реализация извлекает JComboBox и т. Д.)

и я хочу назвать его с классом, полученным во время выполнения, поэтому я просто называю это так:

 public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
    final Class<?> type = field.getType();
    if (type.isEnum())
        return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
    throw new RuntimeException("the rest of the visitor is not necessary here");
}

и я получаю странное сообщение об ошибке: несовместимые типы найдено: java.lang.Object требуется: RETURN_TYPE

расположение сообщения, если сразу после открывающей скобки вызова, перед "t" типа.

если я вызываю его из неуниверсального контекста, он работает:

    Integer extractField(final Extractor<Integer> extractor, final Field field) {
        final Class<?> type = field.getType();
        if (type.isEnum())
            return extractor.extractEnum(type.asSubclass(Enum.class));
        throw new RuntimeException("the rest of the visitor is not necessary here");
    }

У кого-нибудь есть объяснение и решение этой проблемы, пожалуйста?

Вот полный файл для людей, желающих поиграть с ним:

public class Blah {
    interface Extractor<RETURN_TYPE> {
        public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
    }

    public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
        final Class<?> type = field.getType();
        if (type.isEnum())
            return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
        throw new RuntimeException("the rest of the visitor is not necessary here");
    }

    public static Integer extractField(final Extractor<Integer> extractor, final Field field) {
        final Class<?> type = field.getType();
        if (type.isEnum())
            return extractor.extractEnum(type.asSubclass(Enum.class));
        throw new RuntimeException("the rest of the visitor is not necessary here");
    }
}

заранее спасибо,

Nico

Ответы [ 3 ]

3 голосов
/ 09 октября 2009

Я бы не удивился, если бы это была ошибка в вашем компиляторе. Благодаря серьезному использованию обобщений (то, что вы делаете, комбинируя параметризованные методы, ограниченные подстановочные знаки и другие «продвинутые» варианты использования обобщений), я столкнулся с двумя или тремя проблемами в прошлом году в javac (досадно, один и тот же модуль часто отлично скомпилируется в IDE).

В вашем случае я вполне уверен, что это ошибка, поскольку компилятор жалуется на то, что extractor.extractEnum возвращает Object, а не RETURN_TYPE. И независимо от того, какой сумасшедший вывод это делает с вашими аргументами метода enum ... он знает из сигнатуры типа, что Extractor является Extractor<RETURN_TYPE>, поэтому вы должны всегда иметь возможность скажем return extractor.extractEnum(...);.

Доказательством тому является то, что даже если вы вызываете метод с аргументом null (таким образом, полностью устраняя любые возможные осложнения из обобщенных перечислений в аргументе), компилятор все равно будет жаловаться. В частности, теперь говорится, что он думает, что тип возвращаемого значения из Extractor - U<RETURN_TYPE>, что является явно мусором.

Как правило, решение этих проблем заключается в некоторых явных приведениях. Доволен ли компилятор, если вы приведете вывод extractEnum к RETURN_TYPE? Редактировать : нет, это действительно не так - он жалуется, что U<RETURN_TYPE> и RETURN_TYPE необратимы - эээ ...

Если вы используете недавний компилятор 1.6, я советую вам сообщить об этом в Sun, так как это довольно большая проблема с javac. Вот очень короткий тестовый пример, в котором это делается:

public class Test {
  interface Sub<O> {
    public <I extends Enum<I>> O method(final Class<I> enumType);
  }

  public static <O> O go(final Sub<O> sub) {
    return sub.method(null);
  }
}

P.S. Общепринято использовать одну заглавную букву для обозначения параметров общего типа. Я не собираюсь говорить «я прав, вы не правы», но имейте в виду, что я нашел ваш код намного сложнее для чтения и слежения, чем если бы вы использовали вместо этого Extractor. (И, судя по формулировкам Хемаля его ответа, для него тоже самое.)

2 голосов
/ 09 октября 2009

Я не смог вывести исходную проблему.

Правильно ли, что Extractor.extract имеет два параметра типа: U, который должен быть Enum и T, который является произвольным типом? В общем вызове VV - это и T, и U? Если U равно VV, то параметр должен быть Class<VV>, а не Class<Enum>. Следующие компиляции для меня, но, как вы можете видеть, универсальный метод должен быть обеспечивает экземпляр Class<VV>

class Outer {
  static class Extractor<T> {
    public <U extends Enum<U>> T extract(final Class<U> lala) {
      return null;
    }
    // two type parameters, T and U
    // U must be an enum
    // T is arbitrary class
  }

  static <VV extends Enum<VV>> VV extract(final Extractor<VV> extractor, Class<VV> vvClass) {
    final Class<?> type = null;
    return extractor.extract(vvClass);
    // Outer.extract returns VV 
    // T -> VV
    // it seems VV is also U
  }
}
1 голос
/ 09 октября 2009

Похоже, ваш Field.getType( ) вернет только общий класс. Поскольку вы попытаетесь поместить тип с уже стертой информацией о типе, эта функция должна будет выдать предупреждение "unchecked", и ВСЕ информация о типе на универсальном интерфейсе будет стерта.

Помните, что при стирании типа ваш интерфейс выглядит так:

interface Extractor {
    public Object extractEnum( final Class enumType );
}

Таким образом, поскольку вся информация о типе удалена, тип возвращаемого значения extractEnum равен java.lang.Object, поэтому вам необходимо добавить конкретный приведение. И это именно то сообщение об ошибке, которое вы получили.

Вот модифицированный пример вашего кода.

@SuppressWarnings( "unchecked" )
public static <RETURN_TYPE> RETURN_TYPE extractField(
       final Extractor<RETURN_TYPE> extractor,
       final Field field
    )
{
    final Class type = field.getType(); // unchecked

    if (type.isEnum())
    {
        // needs cast
        return (RETURN_TYPE) extractor.extractEnum( type ); // unchecked
    }
    throw new RuntimeException("the rest of the visitor is not necessary here");
}

ОТКЛЮЧИТЬ ЭТОТ КОММЕНТАРИЙ: Чтобы ответить на оригинальный вопрос о том, почему у вас ошибка компиляции. Это квадратный колышек в виде круглой дыры. type.asSubclass( Enum.class ) возвращает Class< ? extends Enum >, это все еще не то же самое, что Class< U >, что ожидает интерфейсный вызов.

...