Dispatch
Overriding vs overloading Notare che esistono due fasi nella selezione del metodo da invocare: Fase statica: risoluzione dell’overloading determina il tipo del metodo, in funzione del tipo statico degli argomenti presenti nel messaggio Fase dinamica: dispatch determina il corpo del metodo, in funzione del tipo dinamico del parametro implicito (ovvero, del destinatario del messaggio)
Polimorfismo – run time Dispatch articolato di quanto abbiamo visto … Metodi dispatch dinamico, con le seguenti eccezioni: metodi private, chiamate via super Metodi di classe (static) dispatch è sempre statico Campi dispatch è sempre statico, sia per variabili di istanza, sia per variabili di classe (o static) Continua…
Invocazione di metodi exp.m(a1,..., an) selezione statica dispatch per definire precisamente l’effetto della chiamata dobbiamo analizzare tre aspetti: selezione statica decide se è corretto invocare m(…) su exp, ovvero se esiste un metodo da invocare determina il tipo del metodo da invocare dispatch determina il corpo del metodo da invocare
Selezione statica exp.m(a1,..., an) Due fasi: Determina il tipo di exp Determina la firma T m(T1,…Tn) del metodo da invocare in base al tipo degli argomenti a1,...,an In questa fase (statica) tipo = tipo statico
Selezione Statica – Fase 1 exp.m(a1,..., an) Determina il tipo statico S di exp: exp = super: S è la superclasse della classe in cui l’invocazione occorre: exp = this: S è la classe in cui l’invocazione occorre in tutti gli altri casi: S è il tipo dichiarato per exp
Selezione statica – Fase 2 exp.m(a1,..., an) exp:S Determina la firma del metodo da invocare calcola il tipo degli argomenti, a1:S1,.... an:Sn seleziona in S il metodo T m(T1,....,Tn) tale che Si <:Ti e m() è accessibile dal contesto di chiamata se S non ha un metodo m() con le caratteristiche desiderate, ripeti il passo 2 sul supertipo di S (ognuno dei supertipi di S ), finché non trovi un metodo oppure esaurisci la gerarchia.
Selezione statica e overloading L’algoritmo appena visto assume che ogni classe contenga al più una versione del metodo da invocare Che succede se esiste una classe contiene più di una versione? Che succede se una classe ed una superclasse contengono diverse versioni dello stesso metodo? Sono entrambi casi di overloading, e la selezione statica deve risolverlo Selezione statica richiede overloading resolution
Selezione statica – Fase 2 rivista exp.m(a1,..., an) exp:S Determina la firma del metodo da invocare calcola il tipo degli argomenti, a1:S1,.... an:Sn determina il best match T m(T1,...,Tn) per l’invocazione m(a1:S1,… an:Sn), a partire da S Se trovi una sola firma ok, altrimenti errore
Best Match exp.m(a1:S1,..., an:Sn) exp:S 1. Determina l’insieme dei metodi applicabili APP(S, m(S1, ..., Sn)) = {U1.m(T11,...,T1n),...,Uk.m(Tk1,...,Tkn)} tali che, per ogni j in [1..k] S <: Uj (quindi: esamina S ed i supertipi di S) m(Tj1, …. Tjn) è definito in Uj ed è visibile nel punto della chiamata exp.m(a1,…,an). Si <: Tji per ogni i in [1..n] 2. Se l’insieme è vuoto fallisci
Best Match Altrimenti, calcola l’insieme dei metodi migliori BEST(S,m(a1:S1,…,. an:Sn)) rimuovi da APP(S, m(a1:S1, ... an:Sn)) ogni Up.m(Tp1, ..., Tpn) tale che esiste un metodo migliore, ovvero un metodo Uq.m(Tq1,...,Tqn) tale che - Uq <:Up (è definito in una superclasse più vicina a S) - Tqi <: Tpi (ha tipi degli argomenti piu vicini agli Si) Se BEST(S,m(a1:S1,…,an:Sn)) contiene più di un metodo, fallisci. Altrimenti l’unico metodo nell’insieme e` il best match per la chiamata exp.m(a1,...,an)
Best Match – Esempi class A { public void m(int i) { System.out.println("A.m(int)"); } } class B extends A { public void m(String s) { System.out.println("B.m(String)"); } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1) b.m(“a string”) ; b.m(1); APP(A, m(int)) = { A.m(int) } APP(B, m(String)) = { B.m(String) } APP(B, m(int)) = { A.m(int) } // APP(A,m(int)) = {A.m(int)} // APP(B,m(String))= {B.m(String)} // APP(B,m(int)) = {A.m(int)}
Best Match – Esempi class A { public void m(int i) { System.out.println("A.m(int)"); } } class B extends A { public void m(String s) { System.out.println("B.m(String)"); } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1) b.m(“a string”) ; a = b; a.m(“a string”); APP(A, m(int)) = { A.m(int) } APP(B, m(String)) = { B.m(String) } APP(A, m(String)) = { } // APP(A,m(int)) = {A.m(int)} // APP(B,m(String))= {B.m(String)} // APP(A,m(string))= {}
Best Match – Esempi class A { public void m(int i) { System.out.println("A.m(int)"); } } class B extends A { public void m(double f) { System.out.println("B.m"); } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1); b.m(1.5); b.m(1); // APP(A,m(int)) = {A.m(int)} // APP(B,m(double))= {B.m(double)} // APP(B,m(int)) = {A.m(int),B.m(double)} // BEST(B.m(int)) = {A.m(int),B.m(double)}
Best Match – Esempi class A { public void m(double g) { System.out.println("A.m"); } } class B extends A { public void m(int i) { System.out.println("B.m"); } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1); b.m(1.5); b.m(1); // APP(A,m(int)) = {A.m(double)} // APP(B,m(double))= {A.m(double)} // APP(B,m(int)) = {A.m(double),B.m(int)} // BEST(B.m(int)) = {B.m(int)}
Best Match – Esempi class A { public void m(int i, float f) { /* just return */} public void m(float f, int i) { /* just return */} } class test { public static void main(String[] args) { A a = new A(); a.m(1, 1); // APP(A, m(int,int)) = { A.m(int,float), A.m(float,int) } // BEST(A,m(int,int)) = { A.m(int,float), A.m(float,int) }
Invocazione di metodi exp.m(a1,..., an) per definire precisamente l’effetto della chiamata dobbiamo analizzare tre aspetti: selezione statica: determina la firma del metodo da invocare calcolo del best match dispatch dinamico: determina il corpo del metodo da invocare se il dispatch è statico esegui il corpo del metodo determinato dalla selezione statica altrimenti esegui il corpo del metodo con il tipo determinato dalla selezione statica che trovi a partire dal tipo dinamico di exp
Esempio // Selezione statica: BEST(A, m(double)) = { A.m(double) } class A { public void m(double d){System.out.println("A.m(double)"); public void m(int i) { System.out.println(“A.m(int)"); } } class B extends A { public void m(double d){System.out.print(“B.m(double)”); } class over { public static void main(String[] args) { { A a = new B(); a.m(1.5); } // Selezione statica: BEST(A, m(double)) = { A.m(double) } // Dispatch: B ridefinisce m(double). Quindi esegui B.m(double)
Esempio // Selezione statica: BEST(A, m(int)) = { A.m(int) } class A { public void m(double d){ System.out.println("A.m(double)"); public void m(int i) { System.out.println(“A.m(int)"); } } class B extends A { public void m(double d){System.out.print(“B.m(double)”); } class over { public static void main(String[] args) { { A a = new B(); a.m(1); } // Selezione statica: BEST(A, m(int)) = { A.m(int) } // Dispatch: B non ridefinisce m(int). Quindi esegui A.m(int)
Esempio // Selezione statica: BEST(A, m(int))={A.m(int),B.m(double)} class A { public void m(double d){ System.out.println("A.m(double)"); public void m(int i) { System.out.println(“A.m(int)"); } } class B extends A { public void m(double d){System.out.print(“B.m(double)”); } class over { public static void main(String[] args) { { B b = new B(); b.m(1); } // Selezione statica: BEST(A, m(int))={A.m(int),B.m(double)}
Metodi private : dispatch statico Essendo private , non sono accessibili alle sottoclassi. Quindi le sottoclassi non possono fare overriding di questi metodi Dispatch può essere deciso dal compilatore Continua…
Metodi private : dispatch statico class A { public String test() { return this.sd() + " , " + this.dd(); } private String sd(){ return "A.sd()"; } public String dd() { return "A.dd()"; } } class B extends A { public String sd() { return "B.sd()"; } public String dd() { return "B.dd()"; } . . . // new B().test() = “A.sd(), B.dd()” // new A().test() = “A.sd(), A.dd()” dispatch statico: A.sd() dispatch dinamico: risolto a run time
Chiamate via super: dispatch statico class A { public String test() { return dd(); } public String dd() { return "A.dd()"; } } class B extends A { public String dd(){ return (super.dd() + " , " + "B.dd()"); } . . . // super.dd() invoca sempre A.dd() // new B().test() = “A.dd(), B.dd()” // new A().test() = “A.dd()”
Campi: dispatch statico class C { String str = "C"; public void m() { System.out.println(str); } } class D extends C { String str = "D"; public void n() { System.out.println(str); } . . . D d = new D(); d.m(); // C d.n(); // D System.out.println(d.str); // D C c = d; c.m(); // C ((D)c).n(); // D System.out.println(c.str); // C