Shark95 ha scritto:
per quanto riguarda le interfacce ho ancora qualche dubbio: non so perché ma non riesco ancora a vederne l'utilità...cioè in teoria dovrebbero semplificarmi la vita, ma poi quando vado a implementarle mi sembra solo uno spreco di tempo.
Non riuscirei a spiegarti l'utilità delle interfacce meglio di quanto non facciano i post precedenti, tra l'altro le uso poche anch'io (nei miei programmi intendo, quelle del framework standard sono molto utili) e dovrei fare molta esperienza prima di essere un "buon consigliere".
Posso darti la visione che ho io fino a questo momento, magari ti sarà utile magari no
Ci sono delle parti di codice in cui non hai bisogno di sapere concretamente il tipo dell'oggetto che hai davanti o come è realizzato, ma ti basta sapere che ha una certa caratteristica, che può fare una determinata cosa etc.
L'interfaccia in questi casi ti viene in aiuto, è un'astrazione posta a un livello superiore che lega in una sorta di vincolo le classi che la implementano, perché tali classi sono costrette ad avere quella certa caratteristica che l'interfaccia si prefigge di avere, o di saper fare quella certa cosa.
Ti è già stato fatto l'esempio di Comparable, per me questo è uno degli esempi più chiari dell'utilità delle interfacce.
Quando ordini una lista, ad esempio, non ti interessa sapere se questa contenga banane, motorini o cos'altro, ti basta sapere che la lista possa essere ordinata in un qualche modo, quindi che i suoi elementi possano essere confrontati tra di loro.
Implementare Comparable soddisfa questo vincolo, tu stai dicendo che è possibile confrontare gli elementi di una certa classe, quindi potranno anche essere ordinati.
Potresti chiederti nel tuo esempio a cosa serva un'interfaccia Volatile, visto che stai utilizzando anche una classe astratta Uccello, che può sembrare molto simile a un'interfaccia ma ti permette in più di "definire uno scheletro dei metodi".
Beh per prima cosa deve essere utile per te avere qualcosa che modelli solo il concetto di Volatile, senza nulla di più (ovviamente esistono volatili che non siano anche uccelli).
Nel senso che ci sarà nello specifico qualche metodo in un'altra classe (in generale non è detto che debba esistere per forza subito, può anche essere utile prevederne l'esistenza) che avrà a che fare con un Volatile, ad esempio ricevendolo come argomento di un metodo, aspettandosi di poter richiamare il metodo vola su quel volatile senza preoccuparsi del tipo effettivo di Volatile che avrà concretamente.
Se pensi che questo non possa esserti utile in nessun modo, forse non hai bisogno di scrivere l'interfaccia Volatile.
In secondo luogo potresti chiederti perché non definire Volatile come classe astratta, visto che ti eviterebbe lo "spreco di tempo" di definire il metodo vola nelle classi che potrebbero ereditare la versione base del metodo, senza doverne fare l'ovveride.
Una prima risposta a questa domanda è che le interfacce sono molto più flessibili di una classe astratta: il vincolo di eredità è forte, perché una classe può estendere una sola classe per volta, mentre può implementare diverse interfacce.
Immagina di aver definito una gerarchia di classi in questo modo, dove Volatile è una classe astratta come Uccello :
Volatile -> Uccello extends Volatile -> Pinguino extends Uccello
-> Aquila extends Uccello
-> ....
-> Pipistrello extends Volatile
A un primo sguardo la cosa potrebbe funzionare, la classe Volatile avrà il metodo vola già definito, e le classi che la estendono non dovranno preoccuparsi di ridefirlo, ad eccezione di quelle che devono cambiarne il comportamento, come Pinguino.
Uccello potrebbe aggiungere il metodo becca che non sarà più in volatile. Le sottoclassi poi avranno tutti i loro metodi propri, ad ora è irrilevante.
Ma questo schema con solo ereditarietà è abbastanza fragile, e rischia di rompersi non appena la tua realtà di interessa si allarga di poco.
Immagina ora di avere una realtà di interesse che contiene altri mammiferi oltre al pipistrello, e di aver bisogno di scrivere metodi in Mammifero che debbano essere ereditati da tutti i figli.
Ecco che avresti già rotto la tua gerarchia, tu vorresti che Pipistrello estendesse sia Mammifero che Volatile, per poter ereditare le caratteristiche di entrambi, ed ovviamente non puoi fare in modo che mammifero estenda volatile o viceversa.
Perdona l'esempio, che può sembrare parecchio stupido, ma il succo è che l'ereditarietà può causare una serie tremenda di forzature che le interfacce invece non presentano.
Altri esempi concreti che siano un po' più adeguati non me ne vengono in mente ora, io ad esempio uso molto le interfacce del package java.awt.event, ma se non hai mai visto awt e swing per realizzare applicazioni desktop non hai familiarità con questi argomenti.
Tieni presente che ti ho dato solo un modo di vedere la cosa, anche abbastanza banale magari, qui nel forum ci sono tanti che potranno darti indicazioni ed esempi molto più validi, che sarebbero utili anche a me