Перейти к основному содержимому

Статическая и динамическая привязка в Java

· 3 мин. чтения

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.foreach.binding.Animal - generic animal noise 
com.foreach.binding.Animal - generic animal noise countdown 3
com.foreach.binding.Animal - generic animal noise countdown 2
com.foreach.binding.Animal - generic animal noise countdown 1
com.foreach.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.foreach.binding.AnimalActivity - Animal is eating

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

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

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

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

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

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

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

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

Как всегда, код доступен на GitHub .