Metodi astratti: utilità

di il
6 risposte

Metodi astratti: utilità

Da quello che ho capito i metodi astratti sono creati per essere sottoposti ad override. Da ciò mi sale il dubbio: a che serve definirla se tanto verrà sovrascritta?

6 Risposte

  • Re: Metodi astratti: utilità

    Koyote ha scritto:


    Da quello che ho capito i metodi astratti sono creati per essere sottoposti ad override. Da ciò mi sale il dubbio: a che serve definirla se tanto verrà sovrascritta?
    public abstract class Solido {
        public abstract double calcolaVolume();
        public abstract double calcolaSuperficie();
    }
    Poi potrebbero esserci svariate sottoclassi "concrete", es. Cubo, Sfera, ecc.. dove quei due metodi DEVONO essere implementati concretamente (e seguendo le regole tecniche del overriding).

    Il punto non è "che serve se tanto verranno sovrascritti". All'interno di un oggetto Cubo ci saranno certamente i due calcolaVolume/calcolaSuperficie ridefiniti "giusti" e sensati per un cubo. Ma il vero punto è che quei due metodi sono già noti in Solido !!

    Quindi se un metodo riceve es. un array di oggetti Solido:

    public static double sommaVolumi(Solido[] solidi) { ...... }

    All'interno del metodo so perfettamente che qualunque oggetto ci sia nell'array, avrà sicuramente i calcolaVolume/calcolaSuperficie "concreti", cioè con una implementazione (che poi sia buona/corretta o no ... è un altro discorso), quindi non mi interessa "sapere" che tipo sono (se un Cubo piuttosto che un Sfera). Posso invocare "polimorficamente" il calcolaVolume su ciascuno di essi.
  • Re: Metodi astratti: utilità

    andbin ha scritto:


    Koyote ha scritto:


    Da quello che ho capito i metodi astratti sono creati per essere sottoposti ad override. Da ciò mi sale il dubbio: a che serve definirla se tanto verrà sovrascritta?
    public abstract class Solido {
        public abstract double calcolaVolume();
        public abstract double calcolaSuperficie();
    }
    Poi potrebbero esserci svariate sottoclassi "concrete", es. Cubo, Sfera, ecc.. dove quei due metodi DEVONO essere implementati concretamente (e seguendo le regole tecniche del overriding).

    Il punto non è "che serve se tanto verranno sovrascritti". All'interno di un oggetto Cubo ci saranno certamente i due calcolaVolume/calcolaSuperficie ridefiniti "giusti" e sensati per un cubo. Ma il vero punto è che quei due metodi sono già noti in Solido !!

    Quindi se un metodo riceve es. un array di oggetti Solido:

    public static double sommaVolumi(Solido[] solidi) { ...... }

    All'interno del metodo so perfettamente che qualunque oggetto ci sia nell'array, avrà sicuramente i calcolaVolume/calcolaSuperficie "concreti", cioè con una implementazione (che poi sia buona/corretta o no ... è un altro discorso), quindi non mi interessa "sapere" che tipo sono (se un Cubo piuttosto che un Sfera). Posso invocare "polimorficamente" il calcolaVolume su ciascuno di essi.

    Quindi diciamo che è un fatto di comodità?
  • Re: Metodi astratti: utilità

    85% di Java è solo "comodità" (90% per C++).
    Durante la fase di interprete/compilazione/jit/sti...zzi l'interprete-compilatore-zzz è assai facilitato nel lavoro, poichè se hai una classe con un metodo astratto "vuoto" (perdona il termine non molto preciso) IMMEDIATAMENTE "capirà" che il programma è sbagliato.
  • Re: Metodi astratti: utilità

    Koyote ha scritto:


    Quindi diciamo che è un fatto di comodità?
    No, non c'entra affatto la "comodità".

    Dimentica solo un attimo il "abstract". Ti è chiaro in generale il concetto del polimorfismo e il principio di "override" (ripeto, in generale, non contare abstract ora)?

    Se un tipo B è sottotipo di un tipo A, allora B non può fare cose in meno rispetto ad A. Può fare eventualmente cose in più e/o le stesse cose ma in maniera differente. Insomma, estendendo una classe non puoi "togliere" metodi/campi. Puoi aggiungere metodi/campi nuovi .... o ridefinire (=override) metodi (di istanza chiaramente, non static) già esistenti e noti nel supertipo.

    L'esempio che segue è il solito un po' "stupido" sugli animali (cioè non molto utile realmente) ma serve per capire:
    public class Prova {
        public static void main(String[] args) {
            Animale animale1 = new Gatto();
            Animale animale2 = new Cane();
    
            animale1.mangia();
            animale2.mangia();
        }
    }
    
    class Animale {
        public void mangia() {
            System.out.println("mangia di Animale");
        }
    }
    
    class Gatto extends Animale {
        public void mangia() {
            System.out.println("mangia di Gatto");
        }
    
        public void emettiFusa() {
            System.out.println("emette fusa");
        }
    }
    
    class Cane extends Animale {
        public void mangia() {
            System.out.println("mangia di Cane");
        }
    }
    L'output è:
    mangia di Gatto
    mangia di Cane


    Partiamo da un fatto: Gatto estende Animale, così come Cane estende Animale. Questa è la ereditarietà, cioè il concetto generale che un tipo possa estendere un altro tipo.

    Quindi un oggetto Gatto è di tipo Gatto ma anche di tipo Animale. Si dice che un Gatto "è-un" (in inglese IS-A) Animale. Quindi ovunque è richiesto un Animale, è assolutamente lecito passare/assegnare un oggetto Gatto (o Cane). Questo è il polimorfismo derivante dalla ereditarietà. Esiste anche un polimorfismo più "leggero", quello dato dal overloading, che però non c'entra niente direttamente con la ereditarietà.

    Gatto ha un emettiFusa(), questo è un metodo in più ("nuovo") rispetto ad Animale. Sulla variabile animale1, NON puoi invocare emettiFusa(), nonostante l'oggetto assegnato è un Gatto. Java è un linguaggio tipizzato staticamente, a livello di compilazione effettua tutta una serie di controlli sui tipi. E tra questi, verifica che se vuoi invocare un metodo su un tipo X, il metodo deve essere "noto" ad X.
    Animale non ha un emettiFusa(), quindi su una variabile Animale non è invocabile.

    Ma Animale ha un mangia(), con una sua implementazione di base. Gatto e Cane ridefiniscono questo metodo mettendo in atto il polimorfismo e il principio di override. Il metodo in Gatto/Cane ha la stessa signature e quindi fa effettivamente un override di quello in Animale.

    Nel main il compilatore "sa" solo che su un riferimento di tipo Animale dovrà invocare un metodo con la forma del mangia() (con quel nome e senza argomenti). Questo lo verifica e stabilisce il compilatore.
    Ma la implementazione viene scelta a RUNTIME, basandosi sull'oggetto realmente istanziato. Ad animale1 è assegnato un oggetto Gatto, quindi a runtime il mangia() realmente eseguito è quello che Gatto ha ridefinito e possiede. Non è quello di Animale.

    --------------
    Tutto chiaro fin qui? Torniamo al abstract. Quando vedi la parola chiave abstract devi pensare a qualcosa di incompleto, che come tale non può essere usato direttamente così come è.
    Ci possono essere diversi motivi per cui un elemento (classe/metodo) è incompleto ma tipicamente lo è perché rappresenta qualcosa che è molto astratto/generalizzato e quindi non ha senso poterlo usare direttamente.

    E' il caso del Solido. Rappresenta un concetto molto astratto, non ha molto senso infatti fare new Solido(). Solido di che??? Quindi Solido è abstract. E i suoi calcolaVolume/calcolaSuperficie, idem, non ha molto senso avere una implementazione concreta in Solido. Cosa potrebbero fare?? Quindi anche i metodi sono abstract.

    Dal punto di vista del override, la questione è abbastanza semplice: la classe Gatto NON è obbligata a ridefinire il mangia(). Se gli andasse bene quello di Animale, potrebbe non ridefinirlo affatto!
    Mentre una classe Cubo concreta (non abstract) che estende Solido invece DEVE implementare i calcolaVolume/calcolaSuperficie.
    Alla fine è tutto qui: un metodo abstract è un obbligo (di implementazione) per una sottoclasse concreta.
  • Re: Metodi astratti: utilità

    +m2+
    deve essere un fan delle architetture RISC,
    o precedente ai Veri Programmatori
    (quelli Veri, che masticavano in pausa fisiologica cicli di 10 pagine di listato in Fortran, per migliorarlo di un millisecondo)

    Del tipo: si stava meglio quando si stava peggio
  • Re: Metodi astratti: utilità

    migliorabile ha scritto:


    +m2+
    deve essere un fan delle architetture RISC,
    o precedente ai Veri Programmatori
    (quelli Veri, che masticavano in pausa fisiologica cicli di 10 pagine di listato in Fortran, per migliorarlo di un millisecondo)

    Del tipo: si stava meglio quando si stava peggio
    diciamo che ho visto la storia evolutiva di c++ e java, da quando non esistevano a oggi.
    Nel caso di java il 99% delle varie aggiunte è zucchero sintattico o poco più. Non è che sia male, è solo che per un 'vero programmatore' da assembler a sistema operativo è particolarmente evidente.
    Come mettere la sella su un asino: non diventa un cavallo.
    Rimane un asino con la sella
Devi accedere o registrarti per scrivere nel forum
6 risposte