Decisione di progettazione del compilatore per il richiamo del metodo dinamico

1

Ho chiesto informazioni su Interpretazione del compilatore di overrideing e overloading su StackOverflow , e ho ottenuto buone risposte, ma questo mi ha portato ad un'altra domanda che non sono sicuro sia appropriato per SO, ma penso sia per questo.

Si dovrebbe leggere la domanda originale e risposta accettata , ma forse è comprensibile osservando il seguente codice:

public static void whatIs(Circle s)
{
    System.out.println("Circle");
}
public static void whatIs(Square s)
{
    System.out.println("Square");
}

e tentiamo di chiamare,

whatIs(shapes[0]); //array of Shape objects (interface implemented by Circle,Square)
whatIs(shapes[1]);

otterremo due errori (uno per Square e uno per Circle ) che indicano che:

  • method Driver.whatIs(Square) is not applicable
    • actual argument Shape cannot be converted to Square by method invocation conversion

Come suggerito nella mia domanda, l'utilizzo di instanceof può dare i risultati desiderati e come suggerito dalla risposta:

The compiler could auto generate code like

if (shapes[0] instanceof Circle)
{
    whatIs((Circle) shapes[0]); //prints "Circle"
}

ma, non . Per essere chiari, so che si può usare una classe astratta invece di un'interfaccia per ottenere funzionalità simili, tuttavia nessuno sa perché il compilatore Java non lo farà automaticamente per te? Non sono un tipo da compilatore, ma ho la sensazione che questa non sia una domanda basata sull'opinione pubblica. Presumo che ci sia una buona ragione per questa decisione.

    
posta Steve P. 09.08.2013 - 23:34
fonte

1 risposta

1

Ricorda: Java non fa "invocazione di metodi dinamici", non c'è nulla di dinamico nell'invocare un sovraccarico o una sovrascrittura, queste sono invocazioni decise al momento della compilazione.

Il problema che si sta vedendo è dovuto al fatto che una classe base manca di dettagli delle sottoclassi, quindi il compilatore non può convertire automaticamente una classe base in fase di compilazione in una data sottoclasse (senza un cast forzato esplicito per assicurarsi che sia convertito con possibilità di errore se usi il cast forzato sull'oggetto sbagliato).

So che non sembra la risposta, ma devi riconoscere ciò che effettivamente fa il compilatore. Quando dici:

Shape someShape = functionThatReturnsACircle();
whatIs(someShape);

Il compilatore legge che hai un oggetto di tipo Shape chiamato someShape , e quindi scrive IL per chiamare il generico whatIs(Shape shape) . Non può sapere fino al runtime che someShape sarà un Circle . Anche se utilizzi new Circle() la variabile potrebbe essere riassegnata a Square prima della chiamata a whatIs , in realtà non può conoscere la sottoclasse che risulterà in someShape . Così al momento della compilazione lo compila per chiamare l'unico che conosce ha una firma corrispondente, che è il whatIs che prende una classe base.

Concesso in fase di esecuzione saprà che l'oggetto in memoria è in effetti un Circle , ma il compilatore ha già scritto IL / ByteCode / MachineCode / what-have-you che richiede di chiamare la forma della classe base di whatIs , quindi ciò che è fatto è già fatto lì.

Come per

actual argument Shape cannot be converted to Square by method invocation conversion

Ora che parla di ciò che ho detto all'inizio di questa risposta. Il compilatore semplicemente non può scrivere istruzioni per convertire someShape in Circle perché come stabilito in precedenza, non ha la certezza che sia un Circle . Inoltre, non è possibile convertire naturalmente una classe base in una classe più specifica perché, ad esempio, un Shape ha solo getArea() e non ha Radius quindi non c'è modo di costruire automaticamente il valore della proprietà Radius in base alle informazioni disponibili nella classe base.

    
risposta data 10.08.2013 - 00:07
fonte

Leggi altre domande sui tag