2015-11-04 2 views
0

Я получаю очень странную ошибку компиляции в приведенном ниже Java-коде.Java Generic Тип возвращаемого файла Проблема

У меня есть простой интерфейс, имеющий API с родовым типом возвращаемого значения:

public interface AttributeGenerator { 

    <T> T generateAttribute(Record record); 
} 

Я могу написать реализацию, и это одна отлично компилируется:

public class StringAttributeGenerator implements AttributeGenerator { 

    @Override 
    public String generateAttribute(Record record) { 
      return "Test"; 
    } 
} 

Теперь, позволяет сказать, я добавить еще параметр к указанному интерфейсу.

public interface AttributeGenerator { 

    <T> T generateAttribute(Record record, Set<Integer> indices); 
} 

И я обеспечиваю другую реализацию, как:

public class StringAttributeGenerator implements AttributeGenerator { 

    @Override 
    public String generateAttribute(Record record, Set<Integer> indices) { 
     return "Test"; 
    } 

} 

Компиляция терпит неудачу и жалобы компилятора, что:

Метод не отменяет из своего суперкласса.

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

+0

Первый фрагмент кода не компилировать для меня (JDK 1.8.0_60) – Tunaki

+1

@Tanuki Странно! Он компилируется для меня отлично! Какая у вас ошибка ? – bhuwansahni

+0

Составив первый фрагмент, я получаю: 'Тип безопасности: Тип возврата Строка для generateAttribute (Запись) из типа StringAttributeGenerator нуждается в необработанном преобразовании, чтобы соответствовать T от типа AttributeGenerator', но у меня есть это предупреждение в eclipse. – azurefrog

ответ

3

AFAIK Проблема заключается в том, что в первом случае вы фактически отключите генерики. Это приведет к тому, что T будет принуждено к Object в этом случае и в связи с возвратом типа covariace, возвращающим String, является штраф. Однако это должно вызывать предупреждение, поскольку вы в основном вынуждаете компилятор игнорировать дженерики и использовать «традиционный» способ, который был бы эквивалентен прямому написанию Object generateAttribute(Record record).

Во втором случае, если вы отключите генераторы таким образом, как я описал выше, подпись будет выглядеть как generateAttribute(Record record, Set indices), где второй параметр будет эквивалентен Set<Object> и, следовательно, подпись больше не соответствует.

Также обратите внимание, что, хотя ваш первый фрагмент будет скомпилирован, вы можете получить проблемы во время выполнения, например. если вы сделали что-то вроде этого:

AttributeGenerator unknownGenerator = new StringAttributeGenerator(); 

//You'd get a string and the system would try to cast to Integer, which clearly will fail 
Integer iWontCompile = unknownGenerator.generateAttribute(someRecord); 

Что вы можете сделать, это определить T в интерфейсе, например, как это:

public interface AttributeGenerator<T> { 
    T generateAttribute(Record record, Set<Integer> indices); 
} 

public class StringAttributeGenerator implements AttributeGenerator<String> { 
@Override 
    public String generateAttribute(Record record, Set<Integer> indices) { 
    return "Test"; 
    } 
} 
+0

В настоящее время я не могу найти соответствующий раздел в JLS для этой темы, поэтому, если кто-то не захочет добавить его в качестве комментария, и я поставлю ссылку в ответ. – Thomas

+0

На самом деле, я понимаю последнюю часть, это был эксперимент. Но это означает, что если я хочу потерять безопасность типа возврата, я также потеряю безопасность в аргументах метода для Generics! – bhuwansahni

+0

@bhuwansahni да, вы можете получить только тот или иной. Но если вы хотите пожертвовать безопасностью возвращаемого типа (что может привести к затруднению отслеживания ошибок), почему бы вам просто не предоставить метод, который возвращает 'Object', или объявить общий тип в интерфейсе и определить его как« Object »в реализация?Кроме того, использование вывода типа, как и вы, очень рискованно, так как вы не можете напрямую обращаться к выведенному типу в реализации, и поэтому вам нужно будет доверять вызывающему пользователю использовать правильный тип. – Thomas

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