Каждый класс Java связан с таблицей виртуальных методов , которая содержит «ссылки» на байт-код каждого метода класса. Эта таблица унаследована от суперкласса определенного класса и расширена с учетом новых методов подкласса. Например.,
class BaseClass {
public void method1() { }
public void method2() { }
public void method3() { }
}
class NextClass extends BaseClass {
public void method2() { } // overridden from BaseClass
public void method4() { }
}
результаты в таблицах
BaseClass
1. BaseClass/method1()
2. BaseClass/method2()
3. BaseClass/method3()
NextClass
1. BaseClass/method1()
2. NextClass/method2()
3. BaseClass/method3()
4. NextClass/method4()
Обратите внимание, как таблица виртуальных методов NextClass
сохраняет порядок записей таблицы BaseClass
и просто перезаписывает "ссылку" method2()
, которую она переопределяет.
Таким образом, реализация JVM может оптимизировать вызов invokevirtual
, помня, что BaseClass/method3()
всегда будет третьей записью в таблице виртуальных методов любого объекта, для которого этот метод когда-либо будет вызываться.
При invokeinterface
эта оптимизация невозможна. Например.,
interface MyInterface {
void ifaceMethod();
}
class AnotherClass extends NextClass implements MyInterface {
public void method4() { } // overridden from NextClass
public void ifaceMethod() { }
}
class MyClass implements MyInterface {
public void method5() { }
public void ifaceMethod() { }
}
Эта иерархия классов приводит к таблицам виртуальных методов
AnotherClass
1. BaseClass/method1()
2. NextClass/method2()
3. BaseClass/method3()
4. AnotherClass/method4()
5. MyInterface/ifaceMethod()
MyClass
1. MyClass/method5()
2. MyInterface/ifaceMethod()
Как видите, AnotherClass
содержит метод интерфейса в своей пятой записи, а MyClass
содержит его во второй записи. Чтобы действительно найти правильную запись в таблице виртуальных методов, вызов метода с invokeinterface
всегда должен будет искать всю таблицу без шансов на стиль оптимизации, который делает invokevirtual
.
Существуют дополнительные различия, такие как тот факт, что invokeinterface
может использоваться вместе со ссылками на объекты, которые фактически не реализуют интерфейс. Следовательно, invokeinterface
должен будет проверить во время выполнения, существует ли метод в таблице, и потенциально вызвать исключение. Если вы хотите глубже погрузиться в тему, я предлагаю, например, «Эффективная реализация интерфейсов Java: Invokeinterface считается безвредным» .