Рассмотрим этот простой класс Java:
class MyClass {
public void bar(MyClass c) {
c.foo();
}
}
Я хочу обсудить, что происходит на линии c.foo().
Оригинальный, вводящий в заблуждение вопрос
Примечание. Не все это происходит с каждым индивидуальным кодом invokevirtual. Подсказка. Если вы хотите понять вызов Java-метода, не читайте только документацию для invokevirtual!
На уровне байт-кода мясо c.foo() будет invokevirtual opcode и, согласно документации для invokevirtual, более или менее произойдет следующее:
- Посмотрите метод foo, определенный в классе компиляции MyClass. (Это включает в себя сначала разрешение MyClass.)
- Проделайте некоторые проверки, в том числе: Убедитесь, что c не является методом инициализации и убедитесь, что вызов MyClass.foo не будет нарушать защищенные модификаторы.
- Выясните, какой метод на самом деле вызывать. В частности, найдите c тип времени выполнения. Если этот тип имеет foo(), вызовите этот метод и верните его. Если нет, найдите c суперкласс класса runtime; если этот тип имеет foo, вызовите этот метод и верните его. Если нет, просмотрите c суперкласса суперкласса типа c; если этот тип имеет foo, вызовите этот метод и верните его. Etc.. Если подходящий метод не найден, тогда ошибка.
Этап № 3 сам по себе кажется достаточным для определения способа вызова и проверки того, что указанный метод имеет правильные типы аргументов/возвратов. Поэтому мой вопрос заключается в том, почему в первую очередь выполняется шаг №1. Возможные ответы:
- У вас недостаточно информации для выполнения шага № 3 до тех пор, пока не будет выполнен шаг №1. (Это кажется невероятным с первого взгляда, поэтому, пожалуйста, объясните.)
- Проверки модификатора ссылок или доступа, выполненные в # 1 и # 2, необходимы для предотвращения некоторых плохих вещей, и эти проверки должны выполняться на основе типа времени компиляции, а не иерархии типов выполнения. (Пожалуйста, объясните.)
Пересмотренный вопрос
Ядром вывода компилятора javac для строки c.foo() будет такая инструкция:
invokevirtual i
где я - индекс для пула постоянной среды MyClass. Эта константная запись пула будет иметь тип CONSTANT_Methodref_info и укажет (возможно, косвенно) A) имя метода (т.е. foo), B) подпись метода и C) имя класса времени компиляции, вызываемого методом on (т.е. MyClass).
Вопрос в том, зачем нужна ссылка на тип времени компиляции (MyClass)? Поскольку invokevirtual собирается выполнять динамическую отправку по типу времени выполнения c, не избыточно ли хранить ссылку на класс времени компиляции?