Как трактуется параметр типа метода, если он носит то же имя, что и параметр окружающего обобщённого класса?
Это новый, независимый параметр типа метода, который скрывает параметр типа класса в области видимости метода. Совпадение имён не устанавливает между ними связь: метод может работать с типом, не совпадающим с параметром экземпляра класса.
В accept внутренний T — это параметр метода, поэтому вызов с Integer корректен, хотя параметр класса T у box равен String.
Generics появились в Java, чтобы описывать типы коллекций и API на этапе компиляции, уменьшая количество небезопасных приведений типов. При этом Java сохранила совместимость с уже существующим байткодом, поэтому обобщения реализованы главным образом через стирание типов.
Правила области видимости параметров типа при этом остаются обычными правилами языка: более внутреннее объявление может скрывать внешнее. Поэтому одинаковые имена параметров типа допустимы, но они не обозначают один и тот же тип.
Совпадающее имя создаёт риск ошибочно решить, что метод связан с типом экземпляра класса. В результате можно объявить API, который внешне выглядит как преобразующий или сохраняющий T класса, но фактически принимает и возвращает совершенно независимый тип.
Неверное понимание особенно опасно при чтении сигнатуры: T в поле класса и T в параметрах обобщённого метода могут иметь разные ограничения, выводиться независимо и использоваться в разных отношениях подтипирования. Ошибка обычно обнаруживается только при необычном вызове или при анализе контракта API.
Параметр типа метода имеет область видимости от своего объявления до конца объявления метода. Если его имя совпадает с именем параметра типа класса, внутреннее объявление затеняет внешнее внутри сигнатуры и тела метода.
В примере Box<T> внешний T определяет тип value и результат getValue. В объявлении accept внутренний T определяет тип аргумента и результата; он выводится отдельно для каждого вызова. Поэтому Box<String> может вызвать accept с Integer.
Чтобы выразить связь с параметром класса, не нужно объявлять одноимённый параметр метода. Сигнатура метода должна использовать T класса напрямую. Если метод действительно работает с независимым типом, лучше назвать его иначе, например U, чтобы намерение было очевидно:
Имена параметров типа не являются частью типа во время выполнения. После стирания имена вообще не участвуют в механизме вызова; их значение — только в читаемости исходного кода и работе компилятора. Границы параметров типа могут различаться, поскольку это также независимые объявления, например внешний T может иметь одну верхнюю границу, а внутренний T — другую.
Практический компромисс таков: повторное имя формально допустимо, но почти всегда ухудшает читаемость. Для публичного API предпочтительны разные имена, а одноимённое затенение оправдано лишь при очень ясном локальном контексте.
В библиотеке есть обобщённый класс преобразователя, параметризованный типом результата. Разработчик объявляет внутри него метод с одноимённым параметром типа и считает, что метод обязан возвращать результат типа класса. На деле вызывающий код может передать один тип и получить другой, что нарушает ожидаемую семантику API.
Вариант с одноимённым параметром краток, но вводит в заблуждение и скрывает ошибку контракта. Вариант с именем U честно показывает независимость типов, однако может выявить, что метод вообще не должен быть обобщённым.
Если метод должен возвращать именно тип класса, выбранное решение — убрать параметр типа метода и использовать T класса. Если метод является самостоятельной операцией над произвольным типом, следует оставить параметр метода, но назвать его U или R. Это делает сигнатуру однозначной и предотвращает вызовы, которые компилятор формально разрешает, но бизнес-смыслом не предусмотрены.
Нет. Это не переопределение и не изменение внешнего параметра, а новое объявление, скрывающее его в своей области видимости. Ограничения внутреннего параметра применяются только к нему; внешний T класса продолжает существовать отдельно и используется там, где внутренний параметр недоступен.
Нужно не объявлять новый параметр метода, а использовать параметр класса напрямую: метод должен ссылаться на T, объявленный у класса. Тогда для Box<String> этот T будет String, и компилятор проверит соответствие аргументов и результата именно этому типу.
Нет. Имена параметров типа не являются частью сигнатуры метода. Замена <T> на <U> сама по себе не создаёт новую перегрузку; методы, отличающиеся только именами параметров типа, считаются одинаковыми по смыслу сигнатуры и не могут сосуществовать как разные перегрузки.