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; } } }
И подкласс Собака :
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 выполнит статическую привязку.
Метод staticboundmethod не связан с конкретным объектом, а скорее вызывается в Type (класс в Java). Выполнение такого метода происходит незначительно быстрее.
Любой другой метод автоматически является виртуальным методом в Java по умолчанию. JVM разрешает такие методы во время выполнения, и это динамическая привязка.
Точная реализация зависит от JVM, но для этого потребуется подход, подобный C++, когда JVM просматривает виртуальную таблицу, чтобы решить, какой объект будет вызван методом.
3. Заключение
Связывание является неотъемлемой частью языка, реализующего полиморфизм, поэтому важно понимать последствия как статического, так и динамического связывания, чтобы быть уверенным, что наши приложения ведут себя так, как мы хотим.
Однако при таком понимании мы можем эффективно использовать наследование классов, а также перегрузку методов.
Как всегда, код доступен на GitHub .