programming is an art
Привязка — это процесс подключения вызова метода к телу метода или определения типа объекта. В Java существует два типа привязки: статическая привязка и динамическая привязка. Я объяснил их один за другим в этом уроке.
Статическое связывание в Java
1. Когда тип объекта определяется компилятором во время компиляции, он называется статическим или ранним связыванием.
2. Также можно сказать, что при статической привязке вызов метода связан с телом метода во время компиляции.
3. Статическое связывание происходит, когда в классе есть какой-либо закрытый, финальный или статический метод.
Давайте рассмотрим один пример, чтобы понять статическое связывание в Java.
class Demo
System . out . println ( «show» ) ;
public static void main ( String . . . s )
Demo d = new Demo ( ) ;
Выход
Поскольку нет переопределенных методов, очевидно, что все методы будут доступны для объекта локального класса (объекта Demo). Также тип объекта определяется компилятором во время компиляции, поэтому статическое связывание происходит в приведенном выше примере.
Динамическое связывание в Java
1. Когда тип объекта определяется компилятором во время выполнения, он называется динамическим или поздним связыванием. Можно также сказать, что при динамическом связывании вызов метода связан с телом метода во время выполнения.
2. Идентификатор ссылки объекта производного класса хранится в ссылочной переменной базового класса. Мы можем получить доступ только к переопределенным методам, личные методы и переменные производного класса недоступны.
3. Динамическое связывание происходит , когда есть переопределены методы или метод наиважнейшая .
4. Динамическое связывание используется для достижения абстракции. Мы узнаем об абстракции в следующем уроке.
Давайте рассмотрим один пример, чтобы понять динамическое связывание в Java.
class Base
System . out . println ( «Base» ) ;
class Child extends Base
System . out . println ( «Child» ) ;
public static void main ( String . . . s )
Base b = new Child ( ) ;
Выход
ребенок
Как вы можете видеть в приведенном выше примере, мы сохраняем ссылочный идентификатор класса Child в ссылочную переменную базового класса. Когда я вызвал метод show (), вызывается метод класса Child и выводится «Child». Если дочерний класс содержит методы, отличные от show (), к ним нельзя получить доступ с помощью переменной b. Для доступа к ним мы должны либо ввести переменную приведения b, либо создать другую переменную класса Child.
надеюсь, что после прочтения этого урока концепция статического и динамического связывания в Java станет вам ясна. Я также поделился видеоуроком, который поможет вам легко понять эти концепции. Если у вас есть какие-либо сомнения и вы нашли что-то неправильное в вышеприведенном уроке, пожалуйста, сообщите об этом, комментируя ниже.
Что такое раннее и позднее связывание java
Раннее связывание (Early Binding) также известно как статическое связывание. В этом случае компилятор заранее определяет, какой метод будет вызван. Это происходит на этапе компиляции, поэтому также называется статическим связыванием. Раннее связывание используется, когда методы не переопределены.
public class Animal < public void sound() < System.out.println("This is Animal sound"); >> public class Main < public static void main(String[] args) < Animal animal = new Animal(); animal.sound(); // Раннее связывание >>
В этом случае, когда мы вызываем метод sound() для объекта animal, компилятор заранее знает, какую реализацию метода использовать, и связывает вызов с реализацией из класса Animal. Позднее связывание (Late Binding) также известно как динамическое связывание. Этот процесс происходит во время выполнения программы, когда точная реализация метода определяется динамически. Позднее связывание используется в Java для методов, которые были переопределены в подклассах (override).
public class Animal < public void sound() < System.out.println("This is Animal sound"); >> public class Dog extends Animal < @Override public void sound() < System.out.println("This is Dog sound"); >> public class Main < public static void main(String[] args) < Animal animal = new Dog(); animal.sound(); // Позднее связывание >>
Здесь, когда мы вызываем метод sound() для объекта animal, который ссылается на объект типа Dog, конкретная реализация метода определяется во время выполнения. Несмотря на то что тип ссылки — Animal, вызывается переопределённый метод из класса Dog.
Suzuya Jūzō Уровень 42
22 апреля 2023
import java.util.*; public class StaticBinding < public static void main(String[] args) < Collection collection = new HashSet(); //print(collection); /* Если методы static - output: Collection Статические или / и перегруженные методы связываются статически т.е. во время компиляции и по ТИПУ ПЕРЕМЕННОЙ, т.е. "collection" относится к Collection. */ new StaticBinding().print(collection); /* Если методы не static - output: Collection Почему? Здесь тоже самое - нестатические методы перегружены, а соответственно действует ранее связывание, т.е. "collection" относится к Collection. */ >public void print(Collection collection) < System.out.println("Collection"); >public void print(Set set) < System.out.println("Set"); >public void print(HashSet hashSet) < System.out.println("HashSet"); >>
И еще: Полиморфизм поддерживается только для обычных вызовов методов. Например, прямое обращение к полю будет обработано на стадии компиляции (по типу переменной).
Урок №166. Раннее и Позднее Связывания
Как мы уже знаем из предыдущих уроков, выполнение программы в языке C++ происходит последовательно, строка за строкой, начиная с функции main(). Когда компилятор встречает вызов функции, то точка выполнения переходит к началу кода вызываемой функции. Откуда компилятор знает, что это нужно сделать?
При компиляции программы компилятор конвертирует каждый стейтмент программы в одну или несколько строк машинного кода. Каждой строке машинного кода присваивается собственный уникальный адрес. Так же и с функциями: когда компилятор встречает функцию, она конвертируется в машинный код и получает свой адрес.
Связывание — это процесс, который используется для конвертации идентификаторов (таких как имена переменных или функций) в адреса. Хотя связывание используется как для переменных, так и для функций, на этом уроке мы сосредоточимся только на функциях.
Оглавление:
- Раннее связывание
- Позднее связывание
Раннее связывание
Большинство вызовов функций, которые встречает компилятор, являются прямыми вызовами функций. Прямой вызов функции — это стейтмент, который напрямую вызывает функцию. Например:
void printValue ( int value )
std :: cout << value ;
printValue ( 7 ) ; // это прямой вызов функции
Прямые вызовы функций выполняются с помощью раннего связывания. Раннее связывание (или «статическая привязка») означает, что компилятор (или линкер) может напрямую связать имя идентификатора (например, имя функции или переменной) с машинным адресом. Помните, что все функции имеют свой уникальный адрес. Поэтому, когда компилятор (или линкер) встречает вызов функции, он заменяет его инструкцией машинного кода, которая сообщает процессору перейти к адресу функции.
Рассмотрим простую программу-калькулятор, в которой используется раннее связывание:
int add ( int a , int b )
return a + b ;
int subtract ( int a , int b )
return a — b ;
int multiply ( int a , int b )
return a * b ;
std :: cout << "Enter a number: " ;
std :: cin >> a ;
std :: cout << "Enter another number: " ;
std :: cin >> b ;
std :: cout << "Enter an operation (0 = add, 1 = subtract, 2 = multiply): " ;
std :: cin >> op ;
> while ( op < 0 || op >2 ) ;
int result = 0 ;
switch ( op )
// Вызываем конкретную функцию напрямую. Используется раннее связывание
case 0 : result = add ( a , b ) ; break ;
case 1 : result = subtract ( a , b ) ; break ;
case 2 : result = multiply ( a , b ) ; break ;
std :: cout << "The answer is: " << result << std :: endl ;
Поскольку add(a, b) , subtract(a, b) и multiply(a, b) являются прямыми вызовами функций, то компилятор будет использовать раннее связывание. Он заменит вызов add(a, b) инструкцией, которая сообщит процессору перейти к адресу add(). То же самое выполнится и для subtract(a, b) , и для multiply(a, b) .
Позднее связывание
В некоторых программах невозможно знать наперёд, какая функция будет вызываться первой. В таком случае используется позднее связывание (или «динамическая привязка»). В языке C++ для выполнения позднего связывания используются указатели на функции. Вкратце, указатель на функцию — это тип указателя, который указывает на функцию вместо переменной. Функция, на которую указывает указатель, может быть вызвана через указатель и оператор вызова функции. Например, вызовем функцию add():
int add ( int a , int b )
return a + b ;
// Создаем указатель на функцию add
int ( * pFcn ) ( int , int ) = add ;
std :: cout << pFcn ( 4 , 5 ) << std :: endl ; // вызов add(4 + 5)
Вызов функции через указатель на функцию также известен как непрямой (или «косвенный») вызов функции. Следующая программа-калькулятор идентична вышеприведенной программе, за исключением того, что вместо прямых вызовов функций используется указатель на функцию:
int add ( int a , int b )
return a + b ;
int subtract ( int a , int b )
return a — b ;
int multiply ( int a , int b )
return a * b ;
std :: cout << "Enter a number: " ;
std :: cin >> a ;
std :: cout << "Enter another number: " ;
std :: cin >> b ;
std :: cout << "Enter an operation (0 = add, 1 = subtract, 2 = multiply): " ;
std :: cin >> op ;
> while ( op < 0 || op >2 ) ;
// Создаем указатель на функцию с именем pFcn (согласен, синтаксис ужасен)
int ( * pFcn ) ( int , int ) = nullptr ;
// Указываем pFcn указывать на функцию, которую выберет пользователь
switch ( op )
case 0 : pFcn = add ; break ;
case 1 : pFcn = subtract ; break ;
case 2 : pFcn = multiply ; break ;
// Вызываем функцию, на которую указывает pFcn с параметрами a и b.
// Используется позднее связывание
std :: cout << "The answer is: " << pFcn ( a , b ) << std :: endl ;
Здесь мы указываем pFcn указывать на функцию, которую выберет пользователь. Затем мы вызываем через указатель функцию, которую выбрал пользователь. Компилятор не может использовать раннее связывание для выполнения вызова функции pFcn(a, b) , так как он не может наперёд определить, на какую функцию pFcn будет указывать!
Позднее связывание менее эффективное, так как присутствует «посредник» между процессором и функцией. С ранним связыванием процессор может перейти непосредственно к адресу функции. С поздним связыванием процессор должен прочитать адрес, хранящийся в указателе, и только затем перейти к этому адресу. Этот дополнительный шаг и замедляет весь процесс. Однако преимущество позднего связывания заключается в том, что оно более гибкое, нежели раннее связывание, так как не нужно решать, какую функцию следует вызывать до, собственно, запуска самой программы.
На следующем уроке мы рассмотрим, как позднее связывание используется для реализации виртуальных функций.
Позднее связывание «bindLate»
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Обычный метод bind называется «ранним связыванием», поскольку фиксирует привязку сразу же.
Как только значения привязаны – они уже не могут быть изменены. В том числе, если метод объекта, который привязали, кто-то переопределит – «привязанная» функция этого не заметит.
Позднее связывание – более гибкое, оно позволяет переопределить привязанный метод когда угодно.
Раннее связывание
Например, попытаемся переопределить метод при раннем связывании:
function bind(func, context) < return function() < return func.apply(context, arguments); >; > var user = < sayHi: function() < alert('Привет!'); >> // привязали метод к объекту var userSayHi = bind(user.sayHi, user); // понадобилось переопределить метод user.sayHi = function() < alert('Новый метод!'); >// будет вызван старый метод, а хотелось бы - новый! userSayHi(); // выведет "Привет!"
…Привязка всё ещё работает со старым методом, несмотря на то что он был переопределён.
Позднее связывание
При позднем связывании bind вызовет не ту функцию, которая была в sayHi на момент привязки, а ту, которая есть на момент вызова.**
Встроенного метода для этого нет, поэтому нужно реализовать.
Синтаксис будет таков:
var func = bindLate(obj, "method");
obj Объект method Название метода (строка)
function bindLate(context, funcName) < return function() < return context[funcName].apply(context, arguments); >; >
Этот вызов похож на обычный bind , один из вариантов которого как раз и выглядит как bind(obj, «method») , но работает по-другому.
Поиск метода в объекте: context[funcName] , осуществляется при вызове, самой обёрткой.
Поэтому, если метод переопределили – будет использован всегда последний вариант.
В частности, пример, рассмотренный выше, станет работать правильно:
function bindLate(context, funcName) < return function() < return context[funcName].apply(context, arguments); >; > var user = < sayHi: function() < alert('Привет!'); >> var userSayHi = bindLate(user, 'sayHi'); user.sayHi = function() < alert('Здравствуйте!'); >userSayHi(); // Здравствуйте!
Привязка метода, которого нет
Позднее связывание позволяет привязать к объекту даже метод, которого ещё нет!
Конечно, предполагается, что к моменту вызова он уже будет определён ;).
function bindLate(context, funcName) < return function() < return context[funcName].apply(context, arguments); >; > // метода нет var user = < >; // ..а привязка возможна! var userSayHi = bindLate(user, 'sayHi'); // по ходу выполнения добавили метод.. user.sayHi = function() < alert('Привет!'); >userSayHi(); // Метод работает: Привет!
В некотором смысле, позднее связывание всегда лучше, чем раннее. Оно удобнее и надёжнее, так как всегда вызывает нужный метод, который в объекте сейчас.
Но оно влечёт и небольшие накладные расходы – поиск метода при каждом вызове.
Итого
Позднее связывание ищет функцию в объекте в момент вызова.
Оно используется для привязки в тех случаях, когда метод может быть переопределён после привязки или на момент привязки не существует.
Обёртка для позднего связывания (без карринга):
function bindLate(context, funcName) < return function() < return context[funcName].apply(context, arguments); >; >