Domanda

Ieri ho avuto un'intervista telefonica tecnica di due ore (che ho superato, woohoo!), ma ho completamente risolto la seguente domanda riguardante l'associazione dinamica in Java. Ed è doppiamente sconcertante perché qualche anno fa insegnavo questo concetto agli studenti universitari quando ero un TA, quindi la prospettiva che ho dato loro la disinformazione è un po 'inquietante ...

Ecco il problema che mi è stato dato:

/* What is the output of the following program? */

public class Test {

  public boolean equals( Test other ) {
    System.out.println( "Inside of Test.equals" );
    return false;
  }

  public static void main( String [] args ) {
    Object t1 = new Test();
    Object t2 = new Test();
    Test t3 = new Test();
    Object o1 = new Object();

    int count = 0;
    System.out.println( count++ );// prints 0
    t1.equals( t2 ) ;
    System.out.println( count++ );// prints 1
    t1.equals( t3 );
    System.out.println( count++ );// prints 2
    t3.equals( o1 );
    System.out.println( count++ );// prints 3
    t3.equals(t3);
    System.out.println( count++ );// prints 4
    t3.equals(t2);
  }
}

Ho affermato che l'output avrebbe dovuto essere due istruzioni di stampa separate all'interno del metodo equals () sovrascritto: in t1.equals (t3) e t3 .equals (t3) . Il secondo caso è abbastanza ovvio e con il primo caso, anche se t1 ha un riferimento di tipo Object, viene istanziato come test di tipo, quindi l'associazione dinamica dovrebbe chiamare la forma sovrascritta del metodo.

Apparentemente no. Il mio intervistatore mi ha incoraggiato a eseguire il programma da solo, ed ecco, c'era solo un risultato dal metodo ignorato: alla riga t3.equals (t3) .

La mia domanda allora è, perché? Come ho già detto, anche se t1 è un riferimento di tipo Object (quindi l'associazione statica invocherebbe il metodo equals () ), l'associazione dinamica dovrebbe occuparsi di invocare la versione più specifica del metodo in base al tipo istanziato del riferimento. Cosa mi sto perdendo?

È stato utile?

Soluzione

Java utilizza l'associazione statica per i metodi sovraccarichi e l'associazione dinamica per i metodi sostituiti. Nel tuo esempio, il metodo equals è sovraccarico (ha un tipo di parametro diverso da Object.equals ()), quindi il metodo chiamato è associato al tipo riferimento al momento della compilazione.

Qualche discussione qui

Il fatto che si tratti del metodo uguale non è realmente rilevante, a parte il fatto che è un errore comune sovraccaricare anziché sovrascriverlo, di cui si è già a conoscenza in base alla risposta al problema nell'intervista.

Modifica: Una buona descrizione anche qui . Questo esempio mostra invece un problema simile relativo al tipo di parametro, ma causato dallo stesso problema.

Credo che se l'associazione fosse effettivamente dinamica, quindi ogni caso in cui il chiamante e il parametro fossero un'istanza di Test comporterebbe il richiamo del metodo ignorato. Quindi t3.equals (o1) sarebbe l'unico caso che non verrebbe stampato.

Altri suggerimenti

Il metodo uguale a di Test non sostituisce il metodo uguale a di java.lang.Object . Guarda il tipo di parametro! La classe Test sta sovraccaricando uguale a con un metodo che accetta un Test .

Se il metodo è uguale a ha lo scopo di sovrascrivere, dovrebbe usare l'annotazione @Override. Ciò causerebbe un errore di compilazione per segnalare questo errore comune.

È interessante notare che, nel codice Groovy (che potrebbe essere compilato in un file di classe), tutte le chiamate tranne una eseguivano l'istruzione print. (Quello che confronta chiaramente un Test con un Oggetto non chiamerà la funzione Test.equals (Test).) Questo perché Groovy FA una tipizzazione completamente dinamica. Ciò è particolarmente interessante perché non ha alcuna variabile esplicitamente tipizzata in modo dinamico. Ho letto in un paio di punti che questo è considerato dannoso, poiché i programmatori si aspettano che Groovy faccia la cosa java.

Java non supporta la varianza nei parametri, solo nei tipi restituiti.

In altre parole, mentre il tipo restituito in un metodo di sostituzione può essere un sottotipo di quello che era nella sostituzione, ciò non è vero per i parametri.

Se il parametro per uguale a Object in Object è Object, mettere un uguale a qualsiasi altra cosa in una sottoclasse sarà un metodo sovraccarico, non un metodo ignorato. Quindi, l'unica situazione in cui verrà chiamato quel metodo è quando il tipo statico del parametro è Test, come nel caso di T3.

Buona fortuna con il processo del colloquio di lavoro! Mi piacerebbe essere intervistato presso un'azienda che pone questo tipo di domande anziché le solite domande su strutture algo / dati che insegno ai miei studenti.

Penso che la chiave risieda nel fatto che il metodo equals () non è conforme allo standard: accetta un altro oggetto Test, non un oggetto Object e quindi non ignora il metodo equals (). Ciò significa che in realtà l'hai sovraccaricato solo per fare qualcosa di speciale quando ti viene dato Test oggetto mentre gli dà Object object chiama Object.equals (Object o). Cercare quel codice attraverso qualsiasi IDE dovrebbe mostrare due metodi equals () per Test.

Il metodo è sovraccarico anziché overriden. Uguali prendono sempre un oggetto come parametro.

a proposito, hai un oggetto su questo nell'efficace java di Bloch (che dovresti possedere).

Alcune note in Rilegatura dinamica (DD) e Rilegatura static??? (SB) dopo un po 'di ricerca:

1.Timing execute : (Ref.1)

  • DB: in fase di esecuzione
  • SB: tempo del compilatore

2.Usato per :

  • DB: override
  • SB: sovraccarico (statico, privato, finale) (Rif. 2)

Riferimento:

  1. Esegui il resolver medio quale metodo preferisce usare
  2. Perché non è possibile sostituire il metodo con modificatore statico, privato o finale
  3. http: // javarevisited. blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

Se viene aggiunto un altro metodo che sostituisce anziché sovraccaricare, spiegherà la chiamata di associazione dinamica in fase di esecuzione.

/ * Qual è l'output del seguente programma? * /

public class DynamicBinding {
    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside @override: this is dynamic binding");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++);// prints 0
        t1.equals(t2);
        System.out.println(count++);// prints 1
        t1.equals(t3);
        System.out.println(count++);// prints 2
        t3.equals(o1);
        System.out.println(count++);// prints 3
        t3.equals(t3);
        System.out.println(count++);// prints 4
        t3.equals(t2);
    }
}

Ho trovato un articolo interessante sull'associazione dinamica vs. statica. Viene fornito con un pezzo di codice per la simulazione dell'associazione dinamica. Ha reso il mio codice più leggibile.

https://sites.google.com/site/jeffhartkopf/covariance

Vedi anche questa domanda SO, strettamente correlata: Sostituzione del quirk del metodo JAVA equals

La risposta alla domanda " why? " è così che viene definito il linguaggio Java.

Per citare l'articolo di Wikipedia su Covariance e Contravarianza :

  

La covarianza del tipo di ritorno è implementata   nel linguaggio di programmazione Java   versione J2SE 5.0. I tipi di parametro hanno   essere esattamente lo stesso (invariante) per   metodo prioritario, altrimenti il   il metodo è sovraccarico di un parallelo   definizione invece.

Altre lingue sono diverse.

È molto chiaro che qui non esiste il concetto di ignorare. È un metodo che sovraccarica. il metodo Object () della classe Object accetta il parametro di riferimento del tipo Object e questo metodo equal () accetta il parametro del riferimento del tipo Test.

Proverò a spiegarlo attraverso due esempi che sono le versioni estese di alcuni degli esempi che ho trovato online.

public class Test {

    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside of Test.equals ot type Object");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++); // prints 0
        o1.equals(t2);

        System.out.println("\n" + count++); // prints 1
        o1.equals(t3);

        System.out.println("\n" + count++);// prints 2
        t1.equals(t2);

        System.out.println("\n" + count++);// prints 3
        t1.equals(t3);

        System.out.println("\n" + count++);// prints 4
        t3.equals(o1);

        System.out.println("\n" + count++);// prints 5
        t3.equals(t3);

        System.out.println("\n" + count++);// prints 6
        t3.equals(t2);
    }
}

Qui, per le linee con valori di conteggio 0, 1, 2 e 3; abbiamo riferimento di Oggetto per o1 e t1 sul metodo equals () . Pertanto, al momento della compilazione, il metodo equals () dal file Object.class sarà limitato.

Tuttavia, anche se riferimento di t1 è Oggetto , ha inizializzazione di Classe test .
Oggetto t1 = new Test (); .
Pertanto, in fase di esecuzione chiama public boolean equals (Object other) che è un

  

metodo ignorato

. inserisci qui la descrizione dell'immagine

Ora, per i valori di conteggio come 4 e 6, è di nuovo chiaro che t3 che ha riferimento e inizializzazione di Test sta chiamando < metodo code> equals () con parametro come Riferimenti oggetto ed è un

  

metodo sovraccarico

OK!

  

Ancora una volta, per capire meglio quale metodo chiamerà il compilatore, giusto   fai clic sul metodo ed Eclipse evidenzierà i metodi simili   tipi che pensa chiameranno al momento della compilazione. Se non capisce   chiamato in fase di compilazione, questi metodi sono un esempio di metodo   overridding.

 inserisci qui la descrizione dell'immagine

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top