Полиморфизм и динамическая диспетчеризация методов

Последнее обновление: 24.09.2025

Полиморфизм является одной из ключевых концепций объектно-ориентированного программирования и подразумевает, что сущность может принимать различные формы. В языке Java переменные являются полиморфными. Это значит, что переменная базового класса может ссылаться на объект производного класса. Например:

public class Program{
     
	public static void main(String[] args) {
			
		Person tom = new Person("Tom");
		tom.print();

        System.out.println();

		Person sam = new Employee("Sam", "Oracle");
		sam.print();
	}
}
class Person {
    
    private String name;
   
    Person(String name){ this.name = name; }
 
    void print(){
		
		System.out.println("Name: " + name);
	}
}

class Employee extends Person{

    private String company;
    
    Employee(String name, String company) {
    
        super(name);
        this.company = company;
    }
    @Override
    void print(){
        
        super.print();
        System.out.println("Company: " + company);
    }
}

Так как Employee наследуется от Person, то объект Employee является в то же время и объектом Person. Грубо говоря, любой работник предприятия одновременно является человеком. Поэтому переменной типа Person мы можем присвоить ссылку на объект типа Employee.

Person sam = new Employee("Sam", "Oracle");

Однако несмотря на то, что переменная представляет объект Person, виртуальная машина видит, что в реальности она указывает на объект Employee. Поэтому при вызове методов у этого объекта будет вызываться та версия метода, которая определена в классе Employee, а не в Person. Например:

Person sam = new Employee("Sam", "Oracle");
sam.print();  // вызов реализации метода print из класса Employee

При вызове переопределенного метода виртуальная машина динамически находит и вызывает именно ту версию метода, которая определена в подклассе. Данный процесс еще называется dynamic method lookup или динамический поиск метода или динамическая диспетчеризация методов. А установка точной версии метода называется динамическим связыванием (dynamic binding)

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

Если же метод определен с модификаторами private, static, final или является конструктором, то компилятор точно знает, какой метод вызывать. Это называется статическим связыванием (static binding).

Консольный вывод данной программы:

Name: Tom

Name: Sam
Company: Oracle

Однако обратное не верно: мы НЕ можем присвоить переменной производного класса ссылку на объект базового класса, например, следующим образом:

Employee sam = new Person("Sam");

И это логично: не все люди работают и являются работниками некоторого предприятия.

Полиморфизм в параметрах и возвращаемых типам

То обстоятельство, что любой объект производного класса также рассматривается как объект базового класса, позволяет параметру базового типа передавать объекты производных классов. Например:

public class Program{
     
	public static void main(String[] args) {
			
		Person tom = new Person("Tom");
		printPerson(tom);

        System.out.println();

		Employee sam = new Employee("Sam", "Oracle");
		printPerson(sam);
	}

    static void printPerson(Person person){

        person.print();
    }
}
class Person {
    
    private String name;
   
    Person(String name){ this.name = name; }
 
    void print(){
		
		System.out.println("Name: " + name);
	}
}

class Employee extends Person{

    private String company;
    
    Employee(String name, String company) {
    
        super(name);
        this.company = company;
    }
    @Override
    void print(){
        
        super.print();
        System.out.println("Company: " + company);
    }
}

В данном случае в классе Program метод printPerson принимает объект типа Person:

static void printPerson(Person person){

    person.print();
}

Но поскольку объекты производного типа Employee также являются объектами типа Person, то мы можем передать эти объекты в данный метод:

Employee sam = new Employee("Sam", "Oracle");
printPerson(sam); // параметру типа Person передаем объект Employee

То же самое относится к возвращению объекту из метода: если возвращаемый тип представляет базовый класс, то мы можем вернуть из метода объект производного класса. Например:

public class Program{
     
	public static void main(String[] args) {
			
		Person person1 = defaultPerson(0);
		person1.print();

        System.out.println();

		Person person2 = defaultPerson(1);
		person2.print();
	}

    static Person defaultPerson(int type){

        if(type == 0){
            return new Person("Tom");
        }
        return new Employee("Tom", "OOO Undefined");
    }
}
class Person {
    
    private String name;
   
    Person(String name){ this.name = name; }
 
    void print(){
		
		System.out.println("Name: " + name);
	}
}

class Employee extends Person{

    private String company;
    
    Employee(String name, String company) {
    
        super(name);
        this.company = company;
    }
    @Override
    void print(){
        
        super.print();
        System.out.println("Company: " + company);
    }
}

В данном случае в классе Program статический метод defaultPerson возвращает значение типа Person:

static Person defaultPerson(int type){

    if(type == 0){
        return new Person("Tom");
    }
    return new Employee("Tom", "OOO Undefined");
}

Но в зависимости от переданного параметра это может быть как объект типа Person, так и объект типа Employee (который также является и объектом типа Person)

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.