← Назад к вопросам

Что такое статический полиморфизм?

2.0 Middle🔥 181 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Статический полиморфизм

Статический полиморфизм (Compile-Time Polymorphism) — это механизм, при котором решение о том, какой метод или конструктор вызвать, принимается компилятором во время компиляции, а не во время выполнения. В Java это реализуется через перегрузку методов (Method Overloading) и перегрузку операторов (в некоторых языках).

Основная идея

В отличие от динамического полиморфизма, который определяет метод во время выполнения на основе типа объекта, статический полиморфизм использует сигнатуру метода (имя, количество, типы и порядок параметров) для выбора нужной реализации.

1. Перегрузка методов (Method Overloading)

Основной вид статического полиморфизма в Java. Методы с одинаковым именем, но разными параметрами:

Примеры перегрузки

public class Calculator {
    
    // Перегрузка по количеству параметров
    public int add(int a, int b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    // Перегрузка по типам параметров
    public double add(double a, double b) {
        return a + b;
    }
    
    public String add(String a, String b) {
        return a + b;
    }
    
    // Перегрузка по порядку параметров
    public void process(String name, int age) {
        System.out.println(name + " is " + age);
    }
    
    public void process(int age, String name) {
        System.out.println(age + " years old " + name);
    }
}

// Использование
Calculator calc = new Calculator();
calc.add(5, 3);              // Вызовет первый add: 8
calc.add(5, 3, 2);           // Вызовет второй add: 10
calc.add(5.5, 3.2);          // Вызовет третий add: 8.7
calc.add("Hello", "World"); // Вызовет четвёртый add: HelloWorld

Правила перегрузки

Методы считаются перегруженными ТОЛЬКО если отличаются:

  1. Количество параметров
  2. Типы параметров
  3. Порядок параметров

НЕ может быть перегрузкой:

// ❌ Только разные возвращаемые типы — ошибка компиляции!
public int getValue() { return 1; }
public double getValue() { return 1.0; }

// ❌ Только разные имена параметров — ошибка компиляции!
public void print(int x) {}
public void print(int y) {}

// ❌ Только разные модификаторы доступа — ошибка компиляции!
public void method() {}
private void method() {}

2. Автоматическое приведение типов при перегрузке

Когда нет точного совпадения типов, Java пытается привести аргументы:

public class TypePromotion {
    
    public void display(int x) {
        System.out.println("int: " + x);
    }
    
    public void display(double x) {
        System.out.println("double: " + x);
    }
    
    public static void main(String[] args) {
        TypePromotion tp = new TypePromotion();
        
        tp.display(5);    // Точное совпадение: вызовет display(int)
        tp.display(5.5);  // Точное совпадение: вызовет display(double)
        tp.display(5L);   // long → int или double? Выберет int (ближайший)
        tp.display(5.5f); // float → double
    }
}

Порядок приведения типов:

  1. Точное совпадение типов
  2. Приведение к ширящемуся типу (widening): byte → short → int → long → float → double
  3. Приведение с помощью boxing/unboxing
  4. Приведение к суперклассу

3. Перегрузка с varargs

public class VarargOverload {
    
    public void print(String... args) {
        System.out.println("varargs: " + args.length);
    }
    
    public void print(String s) {
        System.out.println("single: " + s);
    }
    
    public static void main(String[] args) {
        VarargOverload vo = new VarargOverload();
        
        vo.print("one");        // Вызовет print(String s) — точное совпадение
        vo.print("one", "two"); // Вызовет print(String... args)
        vo.print();             // Вызовет print(String... args) с пустым массивом
    }
}

4. Перегрузка конструкторов

Конструкторы тоже могут быть перегружены:

public class Person {
    private String name;
    private int age;
    private String email;
    
    // Конструктор 1
    public Person(String name) {
        this.name = name;
        this.age = 0;
    }
    
    // Конструктор 2
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // Конструктор 3
    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

// Использование
Person p1 = new Person("John");
Person p2 = new Person("Jane", 25);
Person p3 = new Person("Bob", 30, "bob@example.com");

Статический vs Динамический полиморфизм

// ========== СТАТИЧЕСКИЙ ПОЛИМОРФИЗМ (Method Overloading) ==========
public class StaticPolymorphism {
    public void show(int x) {
        System.out.println("int: " + x);
    }
    
    public void show(double x) {
        System.out.println("double: " + x);
    }
    
    public static void main(String[] args) {
        StaticPolymorphism sp = new StaticPolymorphism();
        sp.show(5);    // Компилятор знает, какой show() вызвать
        sp.show(5.5);  // Во время компиляции!
    }
}

// ========== ДИНАМИЧЕСКИЙ ПОЛИМОРФИЗМ (Method Overriding) ==========
public class Animal {
    public void sound() {
        System.out.println("Animal sound");
    }
}

public class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("Woof!");
    }
}

public class DynamicPolymorphism {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.sound(); // Во время выполнения определяется,
                        // что это Dog, и вызывается Dog.sound()
    }
}

Сравнительная таблица

АспектСтатическийДинамический
Когда решаетсяВо время компиляцииВо время выполнения
МеханизмПерегрузка методовПереопределение методов
УсловиеРазные сигнатурыНаследование + @Override
ПроизводительностьБыстрее (нет runtime checks)Медленнее
ГибкостьМеньшеБольше

Практические примеры

1. Логирование с разными типами данных

public class Logger {
    public void log(String message) {
        System.out.println("[INFO] " + message);
    }
    
    public void log(Exception e) {
        System.err.println("[ERROR] " + e.getMessage());
        e.printStackTrace();
    }
    
    public void log(String message, int level) {
        System.out.println("[LEVEL " + level + "] " + message);
    }
}

// Использование
Logger logger = new Logger();
logger.log("Application started");
logger.log(new NullPointerException("Data is null"));
logger.log("User action", 2);

2. Builder pattern (альтернатива перегрузке)

public class HttpRequest {
    private String method;
    private String url;
    private Map<String, String> headers;
    private String body;
    
    // Вместо многочисленных конструкторов используем Builder
    public static class Builder {
        private String method = "GET";
        private String url;
        private Map<String, String> headers = new HashMap<>();
        private String body;
        
        public Builder(String url) {
            this.url = url;
        }
        
        public Builder method(String method) {
            this.method = method;
            return this;
        }
        
        public Builder header(String key, String value) {
            headers.put(key, value);
            return this;
        }
        
        public Builder body(String body) {
            this.body = body;
            return this;
        }
        
        public HttpRequest build() {
            HttpRequest request = new HttpRequest();
            request.method = this.method;
            request.url = this.url;
            request.headers = this.headers;
            request.body = this.body;
            return request;
        }
    }
}

// Использование
HttpRequest request = new HttpRequest.Builder("http://api.example.com")
    .method("POST")
    .header("Content-Type", "application/json")
    .body("{\"key\": \"value\"}")
    .build();

Когда избежать чрезмерной перегрузки

// ❌ Слишком много перегрузок — сложно понять и поддерживать
public class Utils {
    public String convert(int x) { return String.valueOf(x); }
    public String convert(double x) { return String.valueOf(x); }
    public String convert(boolean x) { return String.valueOf(x); }
    public String convert(Object x) { return x.toString(); }
    // ... ещё 10 вариантов
}

// ✅ Лучше использовать более специфичные имена или generics
public class Converter {
    public static <T> String toString(T value) {
        return value == null ? "null" : value.toString();
    }
}

Статический полиморфизм — это мощный инструмент для создания гибкого, типобезопасного кода, хотя его не следует использовать без необходимости.