Comments 24
Насчёт пункта 2 — тут вполне логично всё. Если мы используем wildcard, то мы явно заявляем компилятору, что мы не знаем соответствующий тип. ?
до некоторой степени эквивалентен ? extends Object
, что делает Ref<?>
ковариантным; следовательно, методы, которые принимают значение типа-параметра в качестве аргумента вызывать больше нельзя. В противном случае в компиляторе должен быть механизм отслеживания того, откуда какое значение пришло, что в общем случае мне кажется эквивалентным проблеме останова.
Насчёт 3 пункта мне тоже не кажется наличие проблемы очевидной. В конце концов, вы там мешаете дженерики и сырые типы, чего делать очень не рекомендуется. Решение, как вы заметили — не использовать сырые типы. Про сырые типы лучше вообще забыть в аспекте написания кода, это страшенный костыль для обратной совместимости.
Первое это вообще треш, да. Никогда правда с таким не сталкивался.
Нет, тут другое, как мне кажется. Насколько я понимаю, то, что вы написали, применяется когда в сигнатуре метода используется один из типовых параметров. В этом случае стирание логично. В случае же когда метод сам по себе дженериковый и не зависит от типа в классе, где он объявлен, это уже существенно менее логично. Ведь в таком случае же совершенно не важно лля вызова метода, сырой ли тип, на котором метод объявлен, или вообще не дженериковый.
Мне кажется, здесь очень простая логика: любой сырой тип в выражении эквивалентен заявлению "я хочу работать в рамках java <1.5". Просто, понятно, легко запомнить.
Сырые типы оставлены для обратной совместимости. В современном коде их быть не должно — ну либо программист сам за себя в ответе. Спрашивается: зачем тратить ресурсы пытаясь научить компилятор расплетать выражения, комбинирующие сырые и параметризованные типы? Какой реальный профит это даст?
Даже если вам в код пришел сырой тип из какой-нибудь легаси библиотеки — гораздо проще его руками привести к "нужному" параметризованному, и дальше не знать горя (тут должна быть саркастическая усмешка).
В противном случае в компиляторе должен быть механизм отслеживания того, откуда какое значение пришлоА почем бы и нет: https://pastebin.com/ELrG1kdG.
Это, конечно, не java, но примеры систем типов, в которых это отслеживается, существуют.
Удивлён, что скала такое умеет. Это не Dotty, случайно?
А так — да, Dotty (Scala.next в начале).
А, вот оно что. Я пристально за дотти не слежу, получается что там как-то унифицировали типовые параметры и ассоциированные типы? Я ещё удивился синтаксису refA.T
/refB.T
, который вроде как подразумевает что T
это type T
а не параметр. Про ассоциированные типы у меня с самого начала была мысль, да, но тут речь шла про параметры всё-таки)
И в scala зачастую поднимали type members в type parameters — см Aux паттерн в shapeless.
Но если придираться, то можно сказать, что wildcard type в Dotty просто нет: Dropped: Existential Types.
Коллега, извиниие, если грубо, но учите матчасть.
Согласен с предыдущим комментатором: во всех случаях действуют обычные правила Type Erasure. В последнем надо поменять местами ограничения: чистка идёт до первого параметра. Про первый я тоже не совсем понял, но посмотрю, когда буду у компа.
Например, в случае 3, на мой взгляд, спецификация идёт вразрез со здравым смыслом, да и в случае 1 тоже.
А в последнем, если поменять местами ограничения, то отвалятся референсы для другого типа, так что хорошим решением это тоже не назовёшь.
идёт вразрез со здравым смыслом, да и в случае 1 тоже.
Чего бы это вдруг?
Тип переменной clazz
— raw type.
Либо программист может использовать generics и тогда это будет явно указано при декларации clazz
.
Либо же программист попал в тяжёлые жизненные обстоятельства и использовать generics не может, в этом случае и у методов тоже никаких generics быть не может.
И да, обстоятельства бывают разные и часто в проектах можно встретить raw types
Потому что научить компилятор разбирать комбинированные (сырые+параметризованные) выражения — это требует ресурсов, и усложняет и так непростую тему параметризованных типов. Какой профит будет от этой траты ресурсов?
Ответ: профит очень мал. Гораздо проще привести сырой тип руками к нужному параметризованному. Если у вас в проекте легаси код с сырыми типами — сделайте вокруг него обертку-адаптер.
Т.е. моя идея в том, что никто никогда не обещал сохранять сырые типы как "first class citizen". Их время ушло, давай, до свидания. Компилятор поддерживает их для обратной совместимости, но не более.
Вот почему стирание типа T должно тянуть за собой стирание совершенно с ним не пересекающегося типа A?
Ну как же не пересекающегося?
Тип T
параметризует класс, тип A
параметризует метод, вложенный в этот класс.
У меня не получается придумать пример, в котором при декларации типа переменной использовать generics нельзя, а при вызове метода объекта, ссылка на который хранится в данной переменной, — можно.
А раз такого случая нет, то и делать поддержку в компиляторе для этого случая нецелесообразно.
class A<T> {
List<Integer> list = Arrays.asList(1);
}
// Type mismatch: cannot convert from Object to Integer
Integer i = new A().list.get(0);
cheremin выше дал хороший комментарий на этот счёт.
Да, в дженериках встречаются странности, иногда контринтуитивное поведение, но все проблемы разрешаются в compile-time.
Странности Generic типов Java