2009-10-09 6 views
3

У меня все еще есть проблемы с некоторыми угловыми случаями в java generics system.перечисления и общие методы в 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 требуется: возвращаемый_тип

расположения сообщения, если сразу после открытия Braket вызова, до «Т» типа.

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

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"); 
    } 
} 

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

Нико

+1

Ваш вопрос напоминает мне старый мультфильм из арахиса. Линус: «Я думаю, что мой язык не работает! Я пытаюсь сказать brlalalala, но всегда выходит брзазазаа!» Люси: «Я думаю, что вся твоя голова не работает». –

+0

Я должен сказать ... без компилятивного примера игрушек (у которого нет бессмысленных абстракций), это очень сложно понять. –

+0

Джонатан> Весь смысл в том, что он не компилируется. Я добавлю полный класс. – nraynaud

ответ

3

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

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

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

В общем, решение для решения этих проблем бросает некоторые явные приведения. Является ли компилятор счастливым, если вы делаете вывод extractEnum в RETURN_TYPE? Edit: нет, это на самом деле нет - он жалуется, что U<RETURN_TYPE> и RETURN_TYPE являются необратимыми - ЕЕР ...

Если вы используете последние 1.6 компилятор, я предлагаю вам сообщить об этом Солнце, как это довольно большой проблема с 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. (И, судя по выражению Гемала о его ответе, для него тоже одно и то же.)

+0

Хорошо, спасибо за ваш глубокий анализ. Но у меня есть яблоко, поэтому я предполагаю, что они отбросят меня. Я покупаю ваше объяснение без проверки, это имеет смысл. Я добавил длинное имя типа переменной для ясности, потому что люди могут не понимать, в чем смысл этой переменной, в моем коде я тоже использую одну букву, даже если я не уверен, что это соглашение действительно хорошо, оно широко принято. – nraynaud

2

Я не был в состоянии вывести исходную задачу.

Я правильно, что 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 
    } 
} 
+0

Нет, извините за то, что заявили о своей проблеме в трудном для понимания способом, пока я не могу найти упрощения. так, T - тип возвращаемого значения экстрактора, а U - тип статического перечисления. В моей точке использования я создаю метод, который по-прежнему является общим для извлеченного типа данных, но получает тип перечисления, который должен быть объявлен из Field.getType(). – nraynaud

1

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

Помните, что с типом стиранием ваш интерфейс выглядит следующим образом:

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>, которого ожидает интерфейс.

+0

да, мне все равно придется иметь дело с предупреждением в этом классе. Но это не дает мне объяснения, почему у меня эта ошибка. – nraynaud

+0

BTW, в моей среде (Eclipse, JDK 1.6.0) второй метод с явным типом Integer тоже не скомпилировался. Конкретное сообщение об ошибке может отличаться в вашей системе, но основная проблема такая же, как я описал в последнем абзаце. –

+0

Я могу сконцентрировать все предупреждения в одном заявлении, спасибо за идею: (RETURN_TYPE) extractor.extractEnum ((Класс)) Но я не думаю, что ваша идея квадратного привязки правильная. посмотрите на это: http://stackoverflow.com/questions/1458779/how-to-cast-to-crtp-in-java и он работает, я использую его 2-3 раза в своем коде. У вас есть предупреждения, связанные с ошибками? – nraynaud

Смежные вопросы