Virtual методы — это методы, которые могут быть переопределены в типах
данных, производных от типа, в котором они были объявлены, позволяющие
полиморфизм. В отличие от
Abstract
методов, virtual методы должны иметь реализацию метода, которая
используется, когда
virtual не переопределяется.
Производный тип может переопределять виртуальные методы, объявленные в его
базовом типе, объявив метод с тем же идентификатором и сигнатурой, то есть
одинаковое количество и тип параметров, а так же с одинаковым типом
возвращаемого значения (если таковой имеется) или же соглашеним о вызове (если
есть различие в режиме передачи параметров или соглашение о вызове или в
возвращаемом типе, то возвращается ошибка во время компиляции). Свойства
будучи виртуальным методом не косвенно наследуются, переписывая метод в
производном типе.
При вызове виртуальных методов, компилятор должен сделать поиск виртуальных
таблиц, чтобы выяснить, какой метод должен быть вызван для данного объекта.
Это требует дополнительных скрытых полей указателей для виртуальных таблиц,
которые будут добавлены в верхней части каждого типа с виртуальными
методами. Этот скрытый vptr обеспечивается встроенным
Object
типа. Из-за этого, виртуальные методы могут быть объявлены только в типе,
который является прямым или косвенным
Extends Object.
Constructors(конструкторы) не могут быть
виртуальными, поскольку они создают объекты, в то время как виртуальные
методы требуют уже существующий объект с определенным типом. Тип
конструктора вызывает определение во время компиляции из кода.
Кроме того, при вызове виртуального метода в конструкторе, используется
только локальная версия метода. Это потому, что vptr еще не создана для
конструктора производного типа, а только конструктор локального типа.
Destructors могут быть виртуальными при
удалении объекта, манипулируя через указатель на базовый тип, так что
разрушение начинается на самом производном типе и ведет свой путь вниз к
базовому типу. Чтобы сделать это, может понадобится добавление виртуальных
деструкторов с пустым телом в любом месте, где явное разрушение еще не
требуется.
С другой стороны, при вызове виртуального (или абстрактного) метода внутри
деструктора (виртуального или нет), производный метод фактически
используется, потому что vptr и его виртуальные таблицы еще там. Но это не
является надежным, потому что дочерний деструктор, возможно, уже вызывается,
и происходящий вызываемый метод может обращаться к разрушенным элементам.
Для методов-элементов с
Virtual в своей декларации,
Virtual
также может быть указан на соответствующие тела методов, для улучшения
читабельности кода.
Примечание: В многоуровневом наследовании, одноименный метод (тот же
идентификатор и сигнатура) может быть объявлен
Abstract,
Virtual
или обычным (без спецификатора) на каждом уровне иерархии наследования.
Когда есть смешивание спецификаторов, обычный порядок abstract -> virtual -> normal,
сверху вниз в иерархии наследования.
Контроль доступа (
Public/
Protected/
Private)
из переопределенного метода не учитывается внутренним процессом
полиморфизма, а учитывается только для первоначального вызова во время
компиляции.
Производный статический метод не может переопределить базовый
виртуальный/абстрактный метод, но может затенить любой базовый метод
(включая виртуальный/абстрактный)
См. также