Chiarimenti su Ereditarietà

di il
4 risposte

Chiarimenti su Ereditarietà

Salve, sto studiando Java ed ho scaricato i videocorsi del Corso di Java della Università Uninettuno. Nel capitolo dove spiega l'ereditarietà dice che è possibile creare una classe che erediti le variabili e i metodi di un'altra (evitando così di riscriverli nella classe), ma ad un certo punto dice che è possibile istanziare un Oggetto esempio:

Persona Alberto

e inizianizzarlo nella classe figlia (di Persona) per utilizzare i metodi di quest'ultima.... ma andando a scrivere tutto su Eclipse mi da errore. Posto il codice:

public class Human {

	
	public static void main(String[] args)
	{	
		
		Persona Giovanni = new Persona();
		Giovanni.seteta(31);
		Giovanni.setnome("Giovanni");
		Giovanni.setaltezza(1.80);
	
		
		System.out.println("L'età di " + Giovanni.getnome() + " e' " + Giovanni.geteta() + " e la sua altezza è " + Giovanni.getaltezza());
		
		Avvocato avv = new Avvocato();
		avv.setnome("Alfio");
		avv.setlavoro("Avvocato");
		
		System.out.println("Nome: " + avv.getnome() + " Lavoro: " + avv.getlavoro());
		
	Persona Lello;
	
	Lello = new Avvocato();
	Lello.setlavoro("Falegname");

	}
 
}


public class Persona

{
	protected int eta;
	protected double altezza;
	protected String nome;
	
	public Persona(){
		System.out.println("Creata una nuova persona");
	}
	
	public String getnome()
	{
		return nome;
	}
	
	public int geteta()
	{
		return eta;
		
	}
	
	public double getaltezza()
	{
		return altezza;
	}
	
	public void seteta(int eta)
	{
	    this.eta = eta;
	}

	public void setaltezza(double altezza)
	{
		this.altezza = altezza;
	}

	public void setnome(String nome)
	{
		this.nome = nome;
	}

	}

public class Avvocato extends Persona
{
	protected String lavoro;

	public String getlavoro()
	{
		return lavoro;
	}
	
	public void setlavoro(String lavoro)
	{
		this.lavoro = lavoro;
	}
}

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 ?


Grazie

4 Risposte

  • Re: Chiarimenti su Ereditarietà

    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.
  • Re: Chiarimenti su Ereditarietà

    Quindi se dichiaro una variabile

    Persona persona1 = new Avvocato();

    posso utilizzare metodi presenti in Avvocato() che abbiano lo stesso nome di quelli presenti in Persona()? Non posso quindi utilizzare metodi che non presenti in Persona() (per questo nell'esempio che ho indicato mi dava errore?)


    Poi come dici se una variabile è dichiarata private vuol dire che è visibile solo in quella classe, ma se la dichiaro protected sarà visibile anche nelle sotto classi che ereditano la classe madre? Ho capito bene?
  • Re: Chiarimenti su Ereditarietà

    Frezza ha scritto:


    Quindi se dichiaro una variabile

    Persona persona1 = new Avvocato();

    posso utilizzare metodi presenti in Avvocato() che abbiano lo stesso nome di quelli presenti in Persona()? Non posso quindi utilizzare metodi che non presenti in Persona() (per questo nell'esempio che ho indicato mi dava errore?)
    Esatto, il compilatore può "vedere" solo i metodi noti alla classe Persona. Non puoi quindi invocare metodi nuovi definiti in sottoclassi.

    Ricorda: il compilatore sceglie solo la "firma" del metodo (tra quelle eventualmente in overload). Ma è a runtime che viene scelta la implementazione del metodo basandosi sull'oggetto realmente istanziato.

    Frezza ha scritto:


    Poi come dici se una variabile è dichiarata private vuol dire che è visibile solo in quella classe, ma se la dichiaro protected sarà visibile anche nelle sotto classi che ereditano la classe madre? Ho capito bene?
    Un membro 'protected' è visibile:
    - da classi/sottoclassi nello stesso package.
    - da sottoclassi in un differente package, solo ed esclusivamente per effetto della ereditarietà, cioè perché eredita il membro.
  • Re: Chiarimenti su Ereditarietà

    In un esempio come il tuo, l'oggetto Persona potrebbe essere visto come un modello "base" per la definizione di oggetti figli (ad esempio Avvocato)

    Potresti quindi dichiarare la classe Persona come astratta, dichiarare il metodo astratto setLavoro, e poi implementare quest'ultimo in tutte le classi derivate. In questo caso il compilatore non ti darà problemi.
Devi accedere o registrarti per scrivere nel forum
4 risposte