Перегрузка методов в Java

Перегрузка (overloading) — это объявление в одном классе нескольких методов с одинаковым именем, но разными списками параметров. Компилятор выбирает нужный метод во время компиляции, исходя из типов и числа аргументов в месте вызова.

Перегрузка делает API класса удобнее: пользователь вызывает «одно и то же действие» в разных формах — например, print(), print(int x), print(String s). В библиотеках Java перегрузка встречается повсюду — System.out.println(...) имеет более десятка перегрузок.

Важно: тип возвращаемого значения не входит в сигнатуру перегрузки. Два метода, различающиеся только типом результата, компилироваться не будут.

Правила различения

Методы можно перегружать, если они отличаются:

  • числом параметров;

  • типами параметров;

  • порядком параметров.

Не различает перегрузку:

  • имя параметра;

  • тип возвращаемого значения;

  • модификаторы (public, static и т. д.).

Пример 1. Различия по числу параметров

public class Led {
    void on() {
        System.out.println("LED ON (default)");
    }

    void on(int pin) {
        System.out.println("LED ON pin=" + pin);
    }

    void on(int pin, int brightness) {
        System.out.println("LED ON pin=" + pin + " brightness=" + brightness);
    }

    public static void main(String[] args) {
        Led led = new Led();
        led.on();
        led.on(13);
        led.on(9, 128);
    }
}

Output:

LED ON (default)
LED ON pin=13
LED ON pin=9 brightness=128

Пример 2. Различия по типам параметров

public class Printer {
    void print(int x) {
        System.out.println("int: " + x);
    }

    void print(double x) {
        System.out.println("double: " + x);
    }

    void print(String x) {
        System.out.println("String: " + x);
    }

    public static void main(String[] args) {
        Printer p = new Printer();
        p.print(42);
        p.print(3.14);
        p.print("hello");
    }
}

Output:

int: 42
double: 3.14
String: hello

Пример 3. Перегрузка конструкторов

public class Sensor {
    String type;
    int pin;

    public Sensor() {
        this("generic", -1);
    }

    public Sensor(String type) {
        this(type, -1);
    }

    public Sensor(String type, int pin) {
        this.type = type;
        this.pin = pin;
    }

    void info() {
        System.out.println(type + " на пине " + pin);
    }

    public static void main(String[] args) {
        new Sensor().info();
        new Sensor("DHT22").info();
        new Sensor("BH1750", 14).info();
    }
}

Output:

generic на пине -1
DHT22 на пине -1
BH1750 на пине 14

Пример 4. Различия по порядку параметров

public class Robot {
    void move(int speed, String direction) {
        System.out.println("speed=" + speed + " dir=" + direction);
    }

    void move(String direction, int speed) {
        System.out.println("dir=" + direction + " speed=" + speed);
    }

    public static void main(String[] args) {
        Robot r = new Robot();
        r.move(150, "forward");
        r.move("backward", 100);
    }
}

Output:

speed=150 dir=forward
dir=backward speed=100

Пример 5. Автоматическое расширение типа

public class Calc {
    static void show(int x) {
        System.out.println("int: " + x);
    }

    static void show(long x) {
        System.out.println("long: " + x);
    }

    static void show(double x) {
        System.out.println("double: " + x);
    }

    public static void main(String[] args) {
        show(10);         // int
        show(10L);        // long
        show(10.0);       // double
        short s = 5;
        show(s);          // расширится до int
    }
}

Output:

int: 10
long: 10
double: 10.0
int: 5

Пример 6. Перегрузка статических методов

public class MathUtils {
    static int max(int a, int b) {
        return a > b ? a : b;
    }

    static int max(int a, int b, int c) {
        return max(max(a, b), c);
    }

    static double max(double a, double b) {
        return a > b ? a : b;
    }

    public static void main(String[] args) {
        System.out.println(max(3, 5));
        System.out.println(max(3, 5, 7));
        System.out.println(max(1.5, 2.7));
    }
}

Output:

5
7
2.7

Подводные камни

  • Перегрузка решается на этапе компиляции по статическим типам аргументов. Это не то же самое, что переопределение (@Override), которое работает на этапе исполнения по фактическому типу объекта.

  • Тип результата не различает перегрузки: int sum(int a) и double sum(int a) в одном классе — ошибка компиляции.

  • При перегрузке с null компилятор может «не понять», какой метод вы хотите — приводите явно: print((String) null).

  • autoboxing и расширение типа создают неочевидные приоритеты выбора метода. Лучше избегать пар вроде f(Integer) и f(long).

  • Сильно перегруженные API трудно читать — иногда лучше дать методам разные имена.

См. также

Примечание

Лицензия и источники

Концепция описана на основе официального Java Tutorial от Oracle (https://docs.oracle.com/javase/tutorial/), Oracle Free Documentation License. Перевод, примеры и пояснения — © AlashEd Wiki.