«1. Введение

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

Если он отображается во время компиляции, это статическое или раннее связывание.

Если проблема разрешается во время выполнения, она называется динамической или поздней привязкой.

2. Понимание с помощью кода

Когда подкласс расширяет суперкласс, он может повторно реализовать определенные в нем методы. Это называется переопределением метода.

Например, давайте создадим суперкласс Animal:

public class Animal {

    static Logger logger = LoggerFactory.getLogger(Animal.class);

    public void makeNoise() {
        logger.info("generic animal noise");
    }

    public void makeNoise(Integer repetitions) {
        while(repetitions != 0) {
            logger.info("generic animal noise countdown " + repetitions);
            repetitions -= 1;
        }
    }
}

И подкласс Dog:

public class Dog extends Animal {

    static Logger logger = LoggerFactory.getLogger(Dog.class);
    
    @Override
    public void makeNoise() {
        logger.info("woof woof!");
    }

}

При перегрузке метода, такого как makeNoise() класса Animal, компилятор разрешит метод и его код во время компиляции. Это пример статической привязки.

Однако, если мы назначим объект типа Dog ссылке типа Animal, компилятор разрешит сопоставление кода функции во время выполнения. Это динамическая привязка.

Чтобы понять, как это работает, давайте напишем небольшой фрагмент кода для вызова классов и их методов:

Animal animal = new Animal();

// calling methods of animal object
animal.makeNoise();
animal.makeNoise(3);

// assigning a dog object to reference of type Animal
Animal dogAnimal = new Dog();

dogAnimal.makeNoise();

The output of the above code will be:
com.baeldung.binding.Animal - generic animal noise 
com.baeldung.binding.Animal - generic animal noise countdown 3
com.baeldung.binding.Animal - generic animal noise countdown 2
com.baeldung.binding.Animal - generic animal noise countdown 1
com.baeldung.binding.Dog - woof woof!

class AnimalActivity {

    public static void eat(Animal animal) {
        System.out.println("Animal is eating");
    }

    public static void eat(Dog dog) {
        System.out.println("Dog is eating");
    }
}

Теперь давайте создадим класс:

AnimalActivity.eat(dogAnimal);

Давайте добавим эти строка в основной класс:

com.baeldung.binding.AnimalActivity - Animal is eating

Вывод будет таким:

Этот пример показывает, что статическая функция подвергается статической привязке.

Причина в том, что подклассы не могут переопределять статические методы. Если бы подкласс реализовал тот же метод, он бы скрыл метод суперкласса. Точно так же, если метод является окончательным или закрытым, JVM выполнит статическую привязку.

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

Любой другой метод автоматически является виртуальным методом в Java по умолчанию. JVM разрешает такие методы во время выполнения, и это динамическая привязка.

Точная реализация зависит от JVM, но для этого потребуется подход, подобный C++, когда JVM просматривает виртуальную таблицу, чтобы решить, для какого объекта будет вызываться метод.

3. Заключение

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

Однако при таком понимании мы можем эффективно использовать наследование классов, а также перегрузку методов.