considerar esta clase Java simple:¿Por qué Java necesita invokevirtual para resolver la clase de tiempo de compilación del método llamado?
class MyClass {
public void bar(MyClass c) {
c.foo();
}
}
quiero hablar de lo que sucede en la línea de c.foo().
original, engañosa Pregunta
Nota: No todo esto realmente sucede con cada código de operación invokevirtual individuo. Sugerencia: si desea comprender la invocación del método Java, ¡no lea solo la documentación de invokevirtual!
A nivel de código de bytes, la carne de c.foo() será el código de operación invokevirtual, y, de acuerdo con the documentation for invokevirtual, más o menos sucederá lo siguiente:
- Busque el método foo definido en tiempo de compilación clase MyClass. (Esto implica la primera resolución de MyClass).
- Realice algunas comprobaciones, que incluyen: Verifique que c no sea un método de inicialización, y verifique que la invocación de MyClass.foo no viole ningún modificador protegido.
- Averigua qué método llamar realmente. En particular, busque c runtime tipo de tiempo de ejecución. Si ese tipo tiene foo(), llama a ese método y regresa. Si no, busca la superclase del tipo de tiempo de ejecución de c; si ese tipo tiene foo, llama a ese método y regresa. Si no, busca la superclase de la superclase del tipo de tiempo de ejecución de c; si ese tipo tiene foo, llama a ese método y regresa. Etc .. Si no se puede encontrar un método adecuado, entonces hay un error.
El paso 3 solo parece adecuado para averiguar qué método utilizar para llamar y verificar que dicho método tenga los tipos de argumento/retorno correctos. Entonces mi pregunta es por qué el paso # 1 se realiza en primer lugar. Las posibles respuestas parecen ser:
- No tiene suficiente información para realizar el paso 3 hasta que se complete el paso # 1. (Esto parece inverosímil a primera vista, así que explíquelo.)
- Las comprobaciones del modificador de acceso o enlace realizadas en los números 1 y 2 son esenciales para evitar que ocurran ciertas cosas malas, y esas comprobaciones deben realizarse en función del tiempo de compilación, en lugar de la jerarquía de tipo de tiempo de ejecución. (Por favor explique.)
revisado Pregunta
El núcleo de la salida del compilador javac para la c.foo línea() será una instrucción como esta:
invokevirtual i
donde i es un índice del grupo de constante de tiempo de ejecución de MyClass. Esa entrada de grupo constante será del tipo CONSTANT_Methodref_info e indicará (tal vez indirectamente) A) el nombre del método llamado (es decir, foo), B) la firma del método, y C) el nombre de la clase de tiempo de compilación que se llama el método encendido (es decir, MyClass).
La pregunta es, ¿por qué es necesaria la referencia al tipo de tiempo de compilación (MyClass)? Dado que invokevirtual va a hacer el despacho dinámico en el tipo de tiempo de ejecución de c, ¿no es redundante almacenar la referencia a la clase en tiempo de compilación?
Esto es debido a la verificación. Ver mi respuesta actualizada, a continuación. –