Frezza ha scritto:
Nella classe principale quando creo la variabile Persona Lello e poi la inizializzo new Avvocato(); se vado a richiamare il metodo setlavoro() (presente solo nella classe Avvocato) mi dice che il metodo non è presente nella classe Persona.... Avrò sbagliato o ho capito male ?
Il comportamento è corretto. Il metodo setlavoro()
non lo puoi invocare su un reference Persona. Quando c'è la invocazione di un metodo, il compilatore Java ha il compito di scegliere la versione (tra quelle eventualmente in overload) di un metodo basandosi sul tipo statico del reference e sul tipo statico degli argomenti.
Java non è come altri linguaggi "dinamici" (es. Javascript o Python) in cui quando invochi un metodo su un oggetto, se capita a runtime che l'oggetto ha davvero quel metodo allora ok, altrimenti hai un qualche errore tipicamente sotto forma di una eccezione.
Nel tuo caso il tipo "statico" del reference è il tipo della variabile, ovvero il tipo Persona. Persona ha un metodo setlavoro() ? No, non c'è l'ha e quindi per il compilatore non è lecito fare la invocazione. Non conta che tu hai assegnato un oggetto Avvocato, che ha il setlavoro().
Guarda questo codice:
public class Prova {
public void metodo(long v) {
System.out.println("invocato metodo(long) di Prova");
}
public void metodo(double v) {
System.out.println("invocato metodo(double) di Prova");
}
}
public class Prova2 extends Prova {
public void metodo(long v) {
System.out.println("invocato metodo(long) di Prova2");
}
}
Se poi tu fai da qualche parte:
Prova p = new Prova2();
p.metodo(12);
Succedono le seguenti cose:
Durante la compilazione, il compilatore deve scegliere quale versione (in pratica la "firma" del metodo) utilizzare. Ci sono due 'metodo', entrambi pubblici quindi accessibili ed entrambi applicabili per il numero di argomenti passato. Uno riceve un long, l'altro un double.
Il compilatore sceglie sempre il metodo più specifico. In questo caso vale la regola basilare del widening. L'argomento 12 è un int e tra long e double quello più vicino a int è long. Pertanto il metodo più specifico è metodo(long).
Questa è la firma del metodo scelto, ripeto, dal compilatore. Quindi nel bytecode generato c'è ben scritto e fisso che la invocazione deve essere verso una forma metodo(long).
Ma la implementazione del metodo viene scelta a runtime, basandosi sull'oggetto realmente istanziato. Dal momento che Prova2 ridefinisce (fa un "override") di metodo(long), e siccome l'oggetto realmente istanziato con
new è Prova2, allora a runtime il metodo che viene effettivamente eseguito è quello di Prova2 ... non quello di Prova.
------
Riguardo la ereditarietà, tieni presente che "ereditare" un membro (campo o metodo) significa sostanzialmente poterlo "vedere" nella sottoclasse. Cioè alla fin fine è una questione di visibilità. Un membro private non è ereditato, perché è visibile solo nella classe dove è definito. Non è visibile direttamente dall'esterno né da sottoclassi.