Considerate questa semplice classe Java:Perché la richiesta invokevirtual di Java per risolvere la classe di compilazione del metodo chiamato?
class MyClass {
public void bar(MyClass c) {
c.foo();
}
}
voglio discutere di ciò che accade sulla linea c.foo().
originale, ingannevole domanda
Nota: Non tutto questo in realtà accade con ogni singolo codice operativo invokevirtual. Suggerimento: se vuoi capire il richiamo del metodo Java, non leggere solo la documentazione per invokevirtual!
A livello bytecode, la carne di c.foo() sarà il codice operativo invokevirtual, e, secondo the documentation for invokevirtual, più o meno il seguente accadrà:
- Cercare il metodo foo definito in compile-time classe MyClass. (Ciò implica prima la risoluzione di MyClass.)
- Effettuare alcuni controlli, tra cui: Verificare che c non sia un metodo di inizializzazione e verificare che la chiamata a MyClass.foo non violi alcun modificatore protetto.
- Calcolare il metodo per chiamare effettivamente. In particolare, cercare il tipo di runtime c. Se quel tipo ha pippo(), chiama quel metodo e ritorna. Altrimenti, cercare la superclasse del tipo runtime di c; se quel tipo ha pippo, chiama quel metodo e ritorna. Altrimenti, cercare la superclasse della superclasse del tipo runtime di c; se quel tipo ha pippo, chiama quel metodo e ritorna. Ecc. Se non è possibile trovare un metodo adatto, allora errore.
Il passaggio n. 3 sembra adeguato per determinare quale metodo chiamare e verificare che il metodo abbia i tipi di argomento/ritorno corretti. Quindi la mia domanda è perché il primo passo viene eseguito in primo luogo. Le risposte possibili sembrano essere:
- Non si dispone di informazioni sufficienti per eseguire il passaggio 3 fino al completamento del passaggio 1. (Questo sembra poco plausibile, quindi, per favore, spiegarlo.)
- I controlli di modifica del collegamento o dell'accesso eseguiti in # 1 e # 2 sono essenziali per evitare che accadano determinate cose negative, e tali controlli devono essere eseguiti in base al tipo di compilazione, piuttosto che la gerarchia di tipi di runtime. (Si prega di spiegare.)
Revised Domanda
Il nucleo dell'uscita javac compilatore per la linea c.foo() sarà un'istruzione come questa:
invokevirtual i
in cui i è un indice del pool costante di runtime di MyClass. La voce di pool costante sarà di tipo CONSTANT_Methodref_info e indicherà (forse indirettamente) A) il nome del metodo chiamato (cioè foo), B) la firma del metodo e C) il nome della classe di tempo di compilazione che il metodo è chiamato on (es. MyClass).
La domanda è: perché è necessario il riferimento al tipo in fase di compilazione (MyClass)? Dal momento che invokevirtual sta per eseguire l'invio dinamico sul tipo di runtime di c, non è ridondante memorizzare il riferimento alla classe di compilazione?
Ciò è dovuto alla verifica. Vedi la mia risposta aggiornata, di seguito. –