Перегрузка методов
Java разрешает определение внутри одного класса двух или более методов с одним именем, если только объявления их параметров различны. В этом случае методы называют перегруженными, а процесс — перегрузкой методов. Перегрузка методов — один из способов поддержки полиморфизма в Java. Тем читателям, которые никогда не использовали язык, допускающий перегрузку методов, эта концепция вначале может показаться странной. Но, как вы вскоре убедитесь, перегрузка методов — одна из наиболее впечатляющих и полезных функциональных возможностей Java.
При вызове перегруженного метода для определения нужной версии Java использует тип и/или количество аргументов метода. Следовательно, перегруженные методы должны различаться по типу и/или количеству их параметров. Хотя возвращаемые типы перегруженных методов могут быть различны, самого возвращаемого типа не достаточно для различения двух версий метода. Когда Java встречает вызов перегруженного метода, она просто выполняет ту его версию, параметры которой соответствуют аргументам, использованным в вызове.
Следующий простой пример иллюстрирует перегрузку метода.
// Демонстрация перегрузки методов.
class OverloadDemo void test () System.out.println(«Параметры отсутствуют»);
// Проверка перегрузки на наличие одного целочисленного параметра.
void test(int а) System.out.println(«а: » + a);
>
// Проверка перегрузки на наличие двух целочисленных параметров.
void test(int a, int b) System.out .println («а и b: » + a + » » + b) ;
>
// Проверка перегрузки на наличие параметра типа double
double test(double a) System.out.println(«double a: » + a);
return a*a;
>
>
class Overload public static void main(String args[]) OverloadDemo ob = new OverloadDemo() ;
double result;
// вызов всех версий метода test()
ob.test () ;
ob.test (10);
ob.test (10, 20);
result = ob.test(123.25) ;
System.out.println(«Результат ob.test(123.25) : » + result);
>
>
Эта программа генерирует следующий вывод:
Параметры отсутствуют
а: 10
а и b: 10 20
double а: 123.25
Результат ob.test(123.25): 15190.5625
Как видите, метод test () перегружается четыре раза. Первая версия не принимает никаких параметров, вторая принимает один целочисленный параметр, третья — два целочисленных параметра, а четвертая — один параметр типа double. То, что четвертая версия метода test () возвращает также значение, не имеет никакого значения для перегрузки, поскольку возвращаемый тип никак не влияет на разрешение перегрузки.
При вызове перегруженного метода Java ищет соответствие между аргументами, которые были использованы для вызова метода, и параметрами метода. Однако это соответствие не обязательно должно быть полным. В некоторых случаях к разрешению перегрузки может применяться автоматическое преобразование типов Java. Например, рассмотрим следующую программу:
// Применение автоматического преобразования типов к перегрузке.
class OverloadDemo void test () System.out.println(«Параметры отсутствуют»);
>
// Проверка перегрузки на наличие двух целочисленных параметров.
void test(int a, int b) System.out.println(«а и b: » + a + » » + b);
class Overload public static void main(String args[]) OverloadDemo ob = new OverloadDemo ();
int i = 88;
ob. test () ;
ob.test(10, 20) ;
ob.test(i);
// этот оператор вызовет test(double)
ob.test(123.2);
// этот оператор вызовет test(double)
>
>
Программа генерирует следующий вывод:
Параметры отсутствуют
а и b: 10 20
Внутреннее преобразование test(double) а: 88
Внутреннее преобразование test(double) а: 123.2
Как видите, эта версия класса OverloadDemo не определяет перегрузку test (int). Поэтому при вызове метода test () с целочисленным аргументом внутри класса Overload какой-то соответствующий метод отсутствует. Однако Java может автоматически преобразовывать тип integer в тип double, и это преобразование может использоваться для разрешения вызова. Поэтому после того, как версия test (int) не обнаружена, Java повышает тип i до double, а затем вызывает метод test (double). Конечно, если бы метод test (int) был определен, вызвался бы он. Java будет использовать автоматическое преобразование типов только при отсутствии полного соответствия.
Перегрузка методов поддерживает полиморфизм, поскольку это один из способов реализации в Java концепции «один интерфейс, несколько методов». Для пояснения приведем следующие рассуждения. В тех языках, которые не поддерживают перегрузку методов, каждому методу должно быть присвоено уникальное имя. Однако часто желательно реализовать, по сути, один и тот же метод для различных типов данных. Например, рассмотрим функцию вычисления абсолютного значения. Обычно в языках, которые не поддерживают перегрузку, существует три или более версии этой функции со слегка различающимися именами. Например, в С функция abs () возвращает абсолютное значение значения типа integer, labs () — значения типа long integer, a fabs () — значения с плавающей точкой. Поскольку язык С не поддерживает перегрузку, каждая функция должна обладать собственным именем, несмотря на то, что все три функции выполняют по существу одно и то же действие. В результате в концептуальном смысле ситуация становится более сложной, чем она есть на самом деле. Хотя каждая из функций построена на основе одной и той же концепции, программист вынужден помнить три имени. В Java подобная ситуация не возникает, поскольку все методы вычисления абсолютного значения могут использовать одно и то же имя. И действительно, стандартная библиотека классов Java содержит метод вычисления абсолютного значения, названный abs (). Перегрузки этого метода для обработки всех численных типов определены в Java-классе Math. Java выбирает для вызова нужную версию метода abs () в зависимости от типа аргумента.
Перегрузка ценна тем, что она позволяет обращаться к схожим методам по общему имени. Таким образом, имя abs представляет общее действие, которое должно выполняться. Выбор нужной конкретной версии для данной ситуации — задача компилятора. Программисту нужно помнить только об общем выполняемом действии. Полиморфизм позволяет свести несколько имен к одному. Хотя приведенный пример весьма прост, если эту концепцию расширить, легко убедиться в том, что перегрузка может облегчить выполнение более сложных задач.
При перегрузке метода каждая версия этого метода может выполнять любые необходимые действия. Не существует никакого правила, в соответствии с которым перегруженные методы должны быть связаны между собой. Однако со стилистической точки зрения перегрузка методов предполагает определенную связь. Таким образом, хотя одно и то же имя можно использовать для перегрузки несвязанных методов, поступать так не следует. Например, имя sqr можно было бы использовать для создания методов, которые возвращают квадрат целочисленного значения и квадратный корень значения с плавающей точкой. Но эти две операции принципиально различны. Такое применение перегрузки методов противоречит ее исходному назначению. В частности, следует перегружать только тесно связанные операции.
Java Challengers #1: Перегрузка методов в JVM
Добро пожаловать в серию статей Java Challengers! Этот серия статей посвящена особенностям программирования на Java. Их освоение — это ваш путь к становлению высококвалифицированным программистом на Java.
Освоение техник, рассматриваемых в этой серии статей требует некоторых усилий, но они будут иметь большое значение в вашем повседневном опыте в качестве java — разработчика. Избежать ошибок проще когда вы знаете как правильно применять основные техники программирования Java и отслеживать ошибки намного проще, когда вы точно знаете, что происходит в вашем java — коде.
Готовы ли вы приступить к освоению основных концепций программирования на Java? Тогда давайте начнем с нашей первой задачки!

Термин «Перегрузка методов»
Про термин перегрузка разработчики склонны думать, что речь идет о перезагрузке системы, но это не так. В программировании, перегрузка метода означает использование одинакового имени метода с разными параметрами.
Что такое перегрузка методов?
Перегрузка методов — это приём программирования, который позволяет разработчику в одном классе для методов с разными параметрами использовать одно и то же имя. В этом случае мы говорим, что метод перегружен.
В Листинге 1 показаны методы с разными параметрами, которые различаются количеством, типом и порядком.
Листинг 1. Три варианта перегрузки методов.
// Количество параметров public class Calculator < void calculate(int number1, int number2) < >void calculate(int number1, int number2, int number3) < >> // Типы параметров public class Calculator < void calculate(int number1, int number2) < >void calculate(double number1, double number2) < >> // Порядок параметров public class Calculator < void calculate(double number1, int number2) < >void calculate(int number1, double number2) < >>
Перегрузка методов и примитивные типы
В Листинге 1 вы видели примитивные типы int и double . Давайте отвлечёмся на минуту и вспомним примитивные типы в Java.
Таблица 1. Примитивные типы в Java
| Тип | Диапазон | Значение по умолчанию | Размер | Примеры литералов |
|---|---|---|---|---|
| boolean | true или false | false | 1 бит | true, false |
| byte | -128… 127 | 0 | 8 бит | 1, -90, -128 |
| char | Символ юникода или от 0 до 65 536 | \u0000 | 16 бит | ‘a’, ‘\u0031’, ‘\201’, ‘\n’, 4 |
| short | -32,768… 32,767 | 0 | 16 бит | 1, 3, 720, 22,000 |
| int | -2 147 483 648… 2 147 483 647 | 0 | 32 бит | -2, -1, 0, 1, 9 |
| long | -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 | 0 | 64 бит | -4000L, -900L, 10L, 700L |
| float | 3.40282347 x 1038, 1.40239846 x 10-45 | 0.0 | 32 бит | 1.67e200f, -1.57e-207f, .9f, 10.4F |
| double | 1.7976931348623157 x 10308, 4.9406564584124654 x 10-324 | 0.0 | 64 бит | 1.e700d, -123457e, 37e1d |
Зачем мне использовать перегрузку методов?
Использование перегрузки делает ваш код чище и проще для чтения, а также помогает избежать ошибок в программе.
В противоположность Листингу 1 представьте программу, где у вас будет много методов calculate() с именами похожими на calculate1 , calculate2 , calculate3 … не хорошо, правда? Перегрузка метода calculate() позволяет использовать одно и то же имя и изменять только то, что необходимо — параметры. Также очень легко найти перегруженные методы, поскольку они сгруппированы в коде.
Чем перегрузка не является
Помните, что изменение имени переменной не является перегрузкой. Следующий код не скомпилируется:
public class Calculator < void calculate(int firstNumber, int secondNumber)<>void calculate(int secondNumber, int thirdNumber)<> >
Вы также не можете перегрузить метод, изменяя возвращаемое значение в сигнатуре метода. Этот код также не скомпилируется:
public class Calculator < double calculate(int number1, int number2)long calculate(int number1, int number2) >
Перегрузка конструктора
Вы можете перегрузить конструктор таким же способом, как и метод:
public class Calculator < private int number1; private int number2; public Calculator(int number1) < this.number1 = number1; >public Calculator(int number1, int number2) < this.number1 = number1; this.number2 = number2; >>
Решите задачку по перегрузке методов
Готовы ли вы к первому испытанию? Давайте выясним!
Начните с внимательного изучения следующего кода.
Листинг 2. Сложная задача по перегрузке методов
public class AdvancedOverloadingChallenge3 < static String x = ""; public static void main(String. doYourBest) < executeAction(1); executeAction(1.0); executeAction(Double.valueOf("5")); executeAction(1L); System.out.println(x); >static void executeAction(int . var) static void executeAction(Integer var) static void executeAction(Object var) static void executeAction(short var) static void executeAction(float var) static void executeAction(double var) >
Хорошо. Вы изучили код. Какой будет вывод?
Правильный ответ приведён в конце статьи.
Что сейчас произошло? Как JVM компилирует перегруженные методы
Для того чтобы понять что произошло в Листинге 2, вам нужно знать несколько вещей о том, как JVM компилирует перегруженные методы.
Прежде всего, JVM разумно ленива: она всегда будет прилагать наименьшие усилия для выполнения метода. Таким образом, когда вы думаете о том, как JVM обрабатывает перегрузку, имейте в виду три важных особенности компилятора:
- Расширение (widening)
- Упаковка (autoboxing and unboxing)
- Аргументы переменной длины (varargs)
Если вы никогда не сталкивались с этими техниками, то несколько примеров должны вам помочь их понять. Обратите внимание, что JVM выполняет их в том порядке, в котором они указаны.
Вот пример расширения:
int primitiveIntNumber = 5; double primitiveDoubleNumber = primitiveIntNumber ;
Это порядок расширения примитивных типов:

(Прим. переводчика — В JLS расширение примитивов описано с большими вариациями, например, long может быть расширен во float или в double.)
int primitiveIntNumber = 7; Integer wrapperIntegerNumber = primitiveIntNumber;
Обратите внимание, что происходит за кулисами при компиляции кода:
Integer wrapperIntegerNumber = Integer.valueOf(primitiveIntNumber);
А вот пример распаковки:
Integer wrapperIntegerNumber = 7; int primitiveIntNumber= wrapperIntegerNumber;
Вот что происходит за кулисами при компиляции этого кода:
int primitiveIntNumber = wrapperIntegerNumber.intValue();
И вот пример метода с аргументами переменной длины. Обратите внимание, что методы переменной длины всегда являются последними для выполнения.
execute(int. numbers)<>
Что такое аргументы переменной длины?
Аргументы переменной длины — это просто массив значений, заданный трёмя точками (. ). Мы можем передать сколько угодно чисел int этому методу.
execute(1,3,4,6,7,8,8,6,4,6,88. ); // Можно продолжать.
Аргументы переменной длины (varargs) очень удобны тем, что значения могут передаваться непосредственно в метод. Если бы мы использовали массивы, нам пришлось бы создать экземпляр массива со значениями.
Расширение: практический пример
Когда мы передаем число 1 прямо в метод executeAction() , JVM автоматически интерпретирует его как int . Вот почему это число не будет передано в метод executeAction(short var) .
Аналогично, если мы передаём число 1.0 JVM автоматически распознает, что это double.
Конечно число 1.0 также может быть и float , но тип таких литералов предопредопределен. Поэтому в Листинге 2 выполняется метод executeAction(double var) .
Когда мы используем обёртку Double , есть два варианта: либо число может быть распаковано в примитивный тип, либо оно может быть расширено в Object . (Помните, что каждый класс в Java расширяет класс Object .) В этом случае JVM выбирает расширение типа Double в Object , потому что это требует меньше усилий, чем распаковка.
Последним мы передаём 1L и так как, мы указали тип — это long .
Распространенные ошибки с перегрузкой
К настоящему времени вы, вероятно, поняли, что с перегрузкой методов всё может быть запутано, поэтому давайте рассмотрим несколько проблем, с которыми вы, вероятно, столкнетесь.
Автоупаковка с обёртками (autoboxing with wrappers)
Java — это строго типизированный язык программирования и, когда мы используем автоупаковку с обёртками, есть несколько вещей, которые мы должны учитывать. Во-первых, следующий код не компилируется:
int primitiveIntNumber = 7; Double wrapperNumber = primitiveIntNumber;
Автоупаковка будет работать только с типом double потому что, когда вы скомпилируете код, он будет эквивалентен этому:
Double number = Double.valueOf(primitiveIntNumber);
Этот код скомпилируется. Первый int будет расширен до double и потом будет упакован в Double . Но при автоупаковке нет расширения типов и конструктор Double.valueof ожидает double , а не int . В этом случае автоупаковка будет работать, если мы сделаем явное приведение типа, например:
Double wrapperNumber = (double) primitiveIntNumber;
Помните, что Integer не может быть Long и Float и не может быть Double . Здесь нет наследования. Каждый из этих типов ( Integer , Long , Float , и Double ) — Number и Object .
Если Вы сомневаетесь, просто помните, что обёртки чисел (wrapper numbers) могут быть расширены до Number или Object . (Есть еще много чего, что можно сказать про обёртки, но оставим это для другой статьи.)
Литералы чисел в коде
Когда мы не указываем тип числа-литерала, JVM вычислит тип за нас. Если напрямую используем число 1 в коде, то JVM создаст его как int . Если мы попытаемся передать 1 напрямую в метод, который принимает short , то он не скомпилируется.
class Calculator < public static void main(String. args) < // Вызов этого метода не скомпилируется // Да, может быть char, short, byte, но JVM создает его как int calculate(1); >void calculate(short number) <> >
Такое же правило будет применяться, когда используется число 1.0 . Хотя это может быть и float , JVM будет считать его double .
class Calculator < public static void main(String. args) < // Вызов этого метода не скомпилируется // Да, может быть float, но JVM создает его как double calculate(1.0); >void calculate(float number) <> >
Другой распространенной ошибкой является предположение, что Double или любая другая обертка лучше подойдет для метода, получающего double .
Факт в том, что JVM требуется меньше усилий для расширения обертки Double в Object вместо её распаковки в примитивный тип double .
Подводя итог, при использовании непосредственно в java-коде, 1 будет int и 1.0 будет double . Расширение — это самый лёгкий путь к выполнению, далее идёт упаковка или распаковка и последней операцией всегда будут методы переменной длины.
Как любопытный факт. Знаете ли вы, что тип char принимает числа?
char anyChar = 127; // Да, это странно, но это компилируется
Что необходимо помнить о перегрузке
Перегрузка — это очень мощная техника для случаев, когда вам нужно одинаковое имя метода с разными параметрами. Это полезная техника, потому что использование правильных имён делает код более удобным для чтения. Вместо того, чтобы дублировать имя метода и добавлять беспорядок в ваш код, вы можете просто перегрузить его.
Это позволяет сохранять код чистым и удобным для чтения, а также снижает риск того, что дублирующие методы сломают часть системы.
Что следует иметь в виду: при перегрузке метода JVM сделает наименьшее усилие из возможных.
Вот порядок самого ленивого пути к исполнению:
- Первое — расширение (widening)
- Второе — упаковка (boxing)
- Третье — аргументы переменной длины (varargs)
Что следует учитывать: сложные ситуации возникают при объявлении чисел напрямую: 1 будет int и 1.0 будет double .
Также помните, что вы можете объявить эти типы явно, используя синтаксис 1F или 1f для float и 1D или 1d для double .
На этом мы закончим о роли JVM в перегрузке методов. Важно понимать, что JVM по своей сути ленива, и всегда будет следовать по самому ленивому пути.
Ответ
Ответ к Листингу 2 — Вариант 3. efce.
Подробнее о перегрузке методов в Java
Введение в классы и объекты для абсолютных новичков, включая небольшие разделы о методах и перегрузке методов.
Узнайте больше о том, почему важно, что Java является строго типизированным языком и изучите примитивные типы Java.
Изучите ограничения и недостатки перегрузки методов, а также способы их устранения путем использования пользовательских типов и объектов параметров.
- Блог компании OTUS
- Программирование
- Java
Урок #23 – Перегрузка методов в языке C#

За счет перегрузки методов мы можем использовать одно имя метода для нескольких разных методов/функций. За урок мы научимся применять эту технологию на практике и узнаем зачем вообще она нужна.
Видеоурок
С перегрузкой методов мы уже ни раз сталкивались, хотя ни разу их не создавали вручную. Пришло время это исправить.
Что такое перегрузка методов?
За счёт перегрузки методов мы можем создать множество методов, что будут принимать разное количество параметров или разный тип данных и при этом будут иметь одно и то же имя.
Отличным примером является метод «Write» или «WriteLine». В данный метод мы можем передавать различные типы данных, но при этом вывод информации происходит без каких-либо ошибок. Так происходит из-за того, что в классе Console есть множество перегрузок методов «Write» и «WriteLine».
Как создать перегрузку?
Чтобы создать перегрузку необходимо прописать метод с тем же именем, но при этом указать либо другое количество принимаемых параметров, либо другие типы данных для принимаемых параметров.
public static string getInfo(string some) < return some + "!"; >public static int getInfo(int a, int b)
Теперь при вызове метода компилятор будет понимать какие параметры были переданы и в зависимости от этого будут вызываться разные методы.
Перегрузка
В пределах одного класса можно определить два или более методов, которые совместно используют одно и тоже имя, но имеют разное количество параметров. Когда это имеет место, методы называются перегруженными , а о процессе говорят как о перегрузке метода . Перегрузка метода – один из способов, с помощью которых реализуется полиморфизм. Перегрузка методов в классах производится по тем же правилам, что и перегрузка функций.
Если для вызываемой функции нет точного соответствия, то компилятор производит поиск подходящей функции по трем уровням последовательно:
- поиск среди методов класса;
- поиск среди методов базовых классов, последовательно от ближайшего предка до самого первого;
- поиск среди остальных функций.
Если точного соответствия ни на одном уровне не найдено, но найдено несколько подходящих функций на разных уровнях, то используется функция, найденная на наименьшем уровне. В пределах одного уровня не может быть более одной подходящей функции.