Допустимо ли объявить в одном классе два метода с одинаковым именем и параметрами, различающихся только возвращаемым типом?
Нет. В Java возвращаемый тип не входит в сигнатуру метода для различения перегрузок, поэтому такие объявления приводят к ошибке компиляции. Перегрузка должна отличаться именем метода или типами, количеством либо порядком параметров.
Java использует статически проверяемый вызов методов: компилятор должен однозначно определить вызываемый метод по имени и аргументам. Возвращаемый тип не используется как самостоятельный критерий выбора, поскольку вызов может находиться в выражении, отдельной инструкции или передаваться дальше без контекста, который надёжно различал бы варианты.
Такой подход отделяет выбор метода от типа результата. При этом во внутреннем формате JVM возвращаемый тип присутствует в дескрипторе метода, но это не делает допустимой перегрузку только по возвращаемому типу на уровне исходного кода Java.
Если разрешить два метода с одинаковыми параметрами и разными результатами, вызов метода без присваивания не имел бы однозначного смысла. Даже в присваивании пришлось бы связывать выбор метода с ожидаемым типом результата, что усложнило бы правила разрешения вызовов и могло бы приводить к неожиданным изменениям при рефакторинге.
Кроме того, изменение только возвращаемого типа нельзя использовать для создания перегруженной версии API. Компилятор рассматривает такие объявления как дублирующие методы.
Сигнатура метода в Java для целей перегрузки определяется именем и параметрами: их количеством, типами и порядком. Возвращаемый тип, модификаторы доступа и список проверяемых исключений не позволяют создать отдельную перегрузку.
Минимальный пример:
При вызове convert компилятор анализирует аргумент String и находит метод по параметрам. Тип переменной, в которую предполагается записать результат, не превращает возвращаемый тип в критерий перегрузки.
Это правило нужно отличать от ковариантного возвращаемого типа при переопределении. Метод потомка может вернуть более конкретный тип, чем метод родителя, но это остаётся переопределением одного метода, а не двумя перегрузками в одном классе.
Если нужны разные варианты преобразования, применяют разные имена методов, разные параметры или обобщённый метод. Компромисс очевиден: API становится немного более явным, зато вызовы остаются предсказуемыми и не зависят от контекста результата.
В библиотеке есть преобразование строки в число. Разработчик пытается добавить вариант, возвращающий long, сохранив те же параметры. Такой подход не компилируется, поэтому возможны два решения.
Первый вариант — разные имена, например toInt и toLong. Он явно сообщает вызывающему коду ожидаемый формат результата, но увеличивает количество имён в API. Второй вариант — добавить параметр, задающий стратегию преобразования, однако это усложняет вызов и может ухудшить читаемость.
Для публичной библиотеки обычно выбирают разные имена: они устраняют неоднозначность, хорошо читаются в коде и не требуют полагаться на тип переменной-приёмника. В результате выбор преобразования виден непосредственно в месте вызова.
Нет, он не позволяет различить методы, совпадающие по имени и параметрам. Если существуют объявления, отличающиеся только возвращаемым типом, ошибка возникает уже при компиляции класса, поэтому до выбора метода по типу переменной дело не доходит.
Тип результата используется после выбора подходящего метода: он проверяется на совместимость с контекстом выражения. Это отдельный этап и не превращает возвращаемый тип в часть правил перегрузки.
Произвольно изменить его нельзя. Возвращаемый тип должен быть тем же самым или ковариантным: для ссылочного типа результат метода потомка может быть подтипом результата метода родителя.
Например, если родитель возвращает Number, потомок может возвращать Integer. Для примитивных типов такой ковариантности нет: int и long не образуют допустимую пару для переопределения.
Проверяемые исключения также не входят в критерий перегрузки. Добавление или изменение throws не создаёт новый метод с точки зрения выбора вызова, поэтому оно не может устранить конфликт.
При переопределении исключения влияют на совместимость методов: потомок не может объявить более широкое проверяемое исключение, чем метод родителя. Это ограничение защищает код, работающий через ссылку на родительский тип, от появления неожиданных обязательных исключений.