2015-11-16 4 views
3

Я получаю ошибку компилятора, вызывающую общий метод с явными параметрами типа, как если бы явный параметр типа не учитывался. Минимальный пример:Явный тип метода метода игнорируется в сыром классе; ошибка компилятора?

class CastExample { 
    static class ThingProducer<S> { 
     public <T> T getThing() { return null; } 
    } 

    static class ThingA {} 

    public static void main(String... args) { 
     ThingProducer thingProducer = new ThingProducer(); 
     ThingA thingA = thingProducer.<ThingA>getThing(); // compile error here 
    } 
} 

ThingProducer является сырьевым типа, поскольку класс имеет параметр типа, но в вызове getThing мы не ссылается параметр типа класса, но вместо предоставления метода параметр типа. За мое понимание JLS, это должно быть законным, но это дает мне эту ошибку:

incompatible types: Object cannot be converted to ThingA 

ошибка исчезает, если я

  • удалить <S> из ThingProducer
  • или сделать getThing статический
  • объявить thingProducer ThingProducer<?> вместо необработанного типа ThingProducer

Является ли это ошибкой компилятора? Если нет, какое правило в JLS определяет это поведение?

+1

'ThingProducer thingProducer' в вашем основном сырье. Плохо плохо плохо. Например, с 'ThingProducer thingProducer', ваш код компилируется в порядке. – Tunaki

+0

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

+0

Где находится вопрос о том, что это дубликат? Я искал, прежде чем спрашивать. –

ответ

1

Section 4.8 of the Java Language Specification отвечает на ваш вопрос:

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

В вашем примере, getThing() является «метод экземпляра ... сырьевого типа С [в этом случае, ThingProducer], которая не передается по наследству». Согласно JLS, его тип является «сырым типом, который соответствует стиранию его типа в общей декларации». В общем объявлении getThing() его тип T неограничен, что означает его стирание java.lang.Object.

Обратите внимание, что спецификация делает не сказать, что тип getThing() «s типа строится путем стирания необработанного типа которого он является членом (то есть, ThingProducer) - это фактически уничтожение самого getThing(), это означает, что оба параметра типа (T и S) стираются.

[Помимо этого: в моем первоначальном ответе я привел другое предложение спецификации: «Ошибка времени компиляции передать аргументы типа нестационарному типу типа необработанного типа, который не унаследован от его суперклассов или суперинтерфейсы ". Мое первоначальное чтение этого предложения состояло в том, что компилятор требовал, чтобы испустить ошибку времени компиляции для вашего синтаксиса выше, так как я пришел к выводу, что вы пытаетесь «передать типы аргументов члену нестатического типа необработанного типа», , Но я изменил мой разум: Я считаю, что последнее предложение имеет в виду нестатической типа элемента (то есть, вложенный тип), а не просто нестатический общий член]

Из. Конечно, не обсуждается раздел 4.8 завершено, не цитируя этот бит из спецификации:

The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of generics into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.

+0

Спасибо за это разъяснение. Я согласен с вашим 2-м чтением, абзац абзаца здесь не применяется. Однако, как вы заметили, спецификация в виде слова говорит, что 'getThing()' на сыром 'ThingProducer' возвращает тип' Object' (независимо от того, какой параметр типа я помещал в вызов метода). Ошибка компиляции, которую мы видим, отражает это. Можно было бы ожидать, что параметры типа метода должны «выжить» для стирания параметров типа класса, но разработчики компилятора уже отклонили запрос об ошибке для этого, см. Http://stackoverflow.com/a/18082751/587365 –

+0

Жирным предложением не применяется. Ошибка компиляции исчезает, если 'thingA' объявляется как тип' Object', хотя параметры типа все еще передаются: 'Object thingA = thingProducer. getThing(); 'Ошибка также исчезает, если' 'изменен на' '. Таким образом, ошибка не в самом вызове метода *, а в последующем приведении/присваивании, как если бы тип метода стирался. – Boann

+0

@Boann: Согласен. Вот что мое редактирование было (см. Бит о «Я передумал»). Полужирная часть имеет в виду члены * type * (то есть вложенный класс) не обычные члены (например, метод здесь). Тем не менее, я не думаю, что это неправильно * включить его в мой ответ, не так ли? –

1

В дополнении к принятому ответу, если вы просто хотите, чтобы исправить ошибку компиляции, как можно проще, и вы не используете класс параметр типа <S>, наиболее подходящее исправление (спасибо @Tunaki) является

ThingProducer<?> thingProducer = new ThingProducer(); 

вместо

ThingProducer thingProducer = new ThingProducer(); 

, которая держит нас в мире генериков, в то время как документально, что оно не имеет значения, что такое параметр типа.

(В этом примере вы могли бы изменить смысл ThingProducer, но я подозреваю, что в реальности любой, кто получает эту ошибку, вероятно, имеет дело с унаследованным классом, который они не могут изменить, - это было моим делом в наименее.)