|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Bannato
Iscritto dal: Mar 2004
Città: Roma
Messaggi: 2688
|
[JAVA] Domanda sul polimorfismo HELPME :-(
Ciao,
a breve ho l'esame di linguaggi di programmazione...ho un piccolo dubbio circa una questione teorica riguardante come funziona il polimorfismo in Java. Praticamente da quello che ho capito il polimorfismo più genuino possibile è il cosidetto polimorfismo universale in cui una variabile (o una routine) può assumere una qualsiasi possibile forma. Tuttavia Java per essere type safe adotta una restrizione a tale polimorfismo che viene detta POLIMORFISMO PER INCLUSIONE ed è basato sul meccanismo dell'ereditarietà mediante il quale è possibile definire sottotipi definendo delle sottoclassi. In tale tipo di polimorfismo, dati un tipo T ed un suo sottotipo S, valgono le seguenti regole: 1) Gli oggetti di tipo S sono particolari oggetti di tipo T che devono soddisfare un certo numero di vincoli aggiuntivi (ad esempio ho una classe padre Rettangolo ed una classe figlia Quadrato...in quest'ultima c'è il vincolo aggiuntivo che base ed altezza devono essere uguali). 2) Tutti gli operatori del supertipo T sono anche operatori definiti per il sottotipo S, viceversa possono esistere operatori definiti per il sottotipo S che non sono definiti per il supertipo T (per esempio Quadrato potrebbe saper fare delle cose in più di Rettangolo come calcolare l'area del cerchio inscritto) 3) Taluni operatori definiti per il supertipo T hanno un comportamento diverso nel sottotipo e li devo andare a ridefinire nel sottotipo S mantenendo la stessa signature (è questo l'overriding degli operatori?). Tramite il polimorfismo per inclusione posso definire una variabile di un certo tipo T e di ASSEGNARLE SOLTANTO IL RIFERIMENTO AD UN OGGETTO IL CUI TIPO è T O UN QUALUNQUE SOTTOTIPO DI T. Possiamo ad esempio definire un metodo la cui signature sarà: nomeMetodo(T parametro) e poi, in qualche altro metodo, invovarlo passandogli come parametro attuale il riferimento ad un oggetto di tipo S dove S è sottotipo di T. Da qua deriva che il compilatore non è in grado di conoscere i tipi effettivi dei parametri attuali che sono potenzialmente infiniti (perchè potrei derivare infiniti tipi di dato da un tipo T). Nonostante il compilatore non sia in grado di conoscere i tipi effettivi degli oggetti è comunque STRONGLY TYPED e ciò è reso possibile proprio dalla restrizione imposta sull'uso delle variabili polimorfiche: "Una volta dichiarata una variabile di un tipo T, all'oggetto referenziato da quella variabile possono essere inviati solo messaggi il cui contenuto è un operatore del tip T e non di qualche suo sottotipo". Per esempio nel main potrei avere qualcosa del genere: Codice:
Rettangolo R = new Quadrato(2); // Definisco R come quadrato ma lo creo come Rettangolo double d = R.raggioCerchioInscritto(); Ok...già quà ho il primo dubbio: praticamente che significa esattamete sta cosa? Semplicemente che ad un metodo definito in Rettangolo posso passare come parametro anche un elemento di tipo Quadrato ma che se dichiaro una variabile di tipo Rettangolo e la creo come Quadrato posso usare solo i metodi definiti in Rettangolo? (non è molto limitante questa cosa? non mi torna...) Tra l'altro se io dichiaro una variabile del supertipo Rettangolo e la creo come Quadrato non gli stò passando un messaggio che contiene il costruttore di Quadrato che è un metodo di Quadrato?!?! Se dichiaro sempre R come Rettangolo ma poi lo costruisco come Quadrato e nella classe figlio ho ridefinito dei metodi presenti nella classe padre...vengono usati i metodi della classe padre? Poi c'è la seconda cosa che non mi torna per niente Mi dice che il polimorfismo in JAVA è particolarmente utile in quanto la JVM esegue il binding dinamico (a runtime) tra invocazioni dei metodi e corpo dei metodi invocati...cioè se in una classe che contiene il main() avessi qualcosa come: Codice:
Rettangolo R; // Dichiaro R come supertipo Rettangolo
if(Math.random()>0) // Se esce un numero positivo
R = new Rettangolo; // allora lo crea come Rettangolo
else
R = new Quadrato; // altrimenti lo crea come il sottotipo Quadrato
R.allarga(4);
Invece in JAVA è la JVM a fare questo binding dinamicamente ed invoca il metodo della classe Quadrato e lo fà in questo modo: controlla il tipo effettivo dell'oggetto referenziato da R ed invoca il metodo allarga() definito per il tipo effettivo che è Quadrato... Ma questo non và palesemente in contrasto con quanto detto prima?!?! Mi riferisco alla restrizione imposta sull'uso delle variabili polimorfiche: "Una volta dichiarata una variabile di un tipo T, all'oggetto referenziato da quella variabile possono essere inviati solo messaggi il cui contenuto è un operatore del tip T e non di qualche suo sottotipo". Come funziona?!?! Mi pare che le due cose siano in conflitto e si contraddicano palesemente... Vi prego aiutatemi sono abbastanza disperato Grazie Andrea |
|
|
|
|
|
#2 | ||||||||
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Quanto segue vale sia per Java, che per C#, che per C++, che per praticamente tutti i linguaggi di programmazione orientati agli oggetti che conosco
Quote:
Quote:
Quote:
Quote:
Quote:
E se scriverai qul codice la sopra, lo starai usando come rettangolo, e in futuro ne potrai richiamare i soli metodi di rettangolo, e i soli campi di rettangolo. Ovviamente scritta cosi' non serve a nulla. Nessuno crea un quadrato e poi lo usa come rettangolo fine a se stesso. Potresti pero' avere un metodo che accetta 3 rettangoli generici, per calcolare una superficie totale. Codice:
double Superficie(Rettangolo[] rect)
{
double ret=0;
for(int t=0; t<rect.Length; t++)
{
ret+=rect[t].GetArea();
}
return ret;
}
Senza il paradigma ad oggetti avresti dovuto creare un metodo che accetta solo quadrati, uno che accetta solo rettangoli, acresti dovuto dividere la tua collezione tra quadrati e rettangoli, avresti dovuto invocare i due metodi separatamente e avresti dovuto alla fine sommare i due risultati. Quote:
Quote:
Quote:
Nell'esempio di prima, la GetArea() richiamata sara' quella di un rettangolo, oppure proprio quella di un quadrato, a seconda che il singolo elemento dell'array sia un rettangolo o proprio un quadrato.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
||||||||
|
|
|
|
|
#3 |
|
Bannato
Iscritto dal: Mar 2004
Città: Roma
Messaggi: 2688
|
Ok...sei stato molto illuminante...e credo di aver capito...ma...
Se io ho un supertipo definito dalla classe Rettangolo ed un sottotipo di tale classe definito dalla classe Quadrato (c'è una relazione "is a", nel senso che un quadrato è un rettangolo e deve soddisfare un vincolo in più: che i lati siano uguali). Dentro la classe Quadrato avrò alcuni metodi di Rettangolo riscritti ed alcuni metodi in più che esprimono cose che un quadrato sa fare e che un rettangolo non sa fare come il calcolo del raggio del cerchio iscritto ad un quadrato. Inizialmente mi si dice che in Java se nel main() avessi qualcosa del tipo: Codice:
Rettangolo R = new Quadrato(); double d = R.raggioCerchioInscritto(); Quì mi viene detto chiaramente che l'invocazione del metoro raggioCerchioInscritto causerà un errore in fase di compilazione perchè tale metodo è definito solo nel sottotipo Quadrato ma la variabile, benchè sia stata costruita come Quadrato, è stata dichiarata di tipo Rettangolo. Poi mi viene detto che se nel main() avessi qualcosa del tipo: Codice:
Rettangolo R; // Dichiaro R come supertipo Rettangolo
if(Math.random()>0) // Se esce un numero positivo
R = new Rettangolo; // allora lo crea come Rettangolo
else
R = new Quadrato; // altrimenti lo crea come il sottotipo Quadrato
R.allarga(4);
Mi si dice che se l'oggetto viene costruito a runtime come Rettangolo viene invocata la versione del metodo definita nella classe Rettangolo, mentre se l'oggetto viene costruito a runtime come Quadrato viene invocata la versione del metodo definita nella classe Quadrato....questo grazie alla JVM che esegue dinamicamente il binding tra invocazione del metodo e corpo del metodo invocante.... Tutto molto bello...ma mi pare in chiara contraddizione con quanto detto nell'esempio precedente in quanto anche in questo secondo caso l'oggetto viene definito come Rettangolo (prima di essere costruito a runtime o come Rettangolo o come Quadrato)... Non dovrebbe valere quanto detto prima?!?! che se definisco la variabile R come Rettangolo anche se ci metto il riferimento ad un oggetto costruito come Quadrato (con tipo effettivo Quadrato) posso usare solo i metodi definiti in rettangolo per la famosa regola restrittiva sulle variabili polimorfe... O l'una o l'altra cosa sono sbagliate...perchè quà dichiaro R come Rettangolo e se poi lo costruisco come Quadrato posso usare la versione del metodo dilata() definita nella classe Quadrato e prima che dichiaravo sempre R come Rettangolo e poi lo costruivo come Quadrato mi dava errore in fase di compilazione se provavo ad eseguire il metodo raggioCerchioInscritto() definito solo nella classe Quadrato?!?!? E' questo che non mi torna...cioè nel secondo esempio, se l'oggetto viene costruito come Quadrato ed il suo riferimento assegnato ad R che è dichiaratadi tipo Rettangolo, viene usato tranquillamente il metodo allarga() ridefinito nella classe Quadrato poichè tale metodo è già nativamente presente nella classe Rettangolo mentre nel primo caso non posso usare il metodo raggioCerchioInscritto poichè questo è definito SOLO nella classe Quadrato? ho capito bene? Se è effettivamente così per quale motivo pratico i progettisti di Java hanno deciso di non farmi usare un metodo definito solo nel tipo effettivo se la variabile è dichiarata con un altro tipo apparente? Grazie Andrea |
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Il dynamic binding viene fatto sulla base del vero tipo di un oggetto, e non sulla base del tipo della dichiarazione.
Quindi quando il vero tipo e' un quadrato, verra' richiamato il metodo Allarga() "eventualmente" ridefinito dentro la classe quadrato. Non e' in contraddizione. Puoi richiamare tutti i metodi che vuoi, purche' siano presenti nella classe Rettangolo. Ma il codice eseguito sara' quello di volta in volta corretto, a seconda del vero tipo dell'oggetto. Non e' in contraddizione, tu stesso avevi detto prima che il metodo Allarga() e' presente sia in classe Rettangolo che in classe Quadrato. Se fosse presente solo in Quadrato, allora avresti un errore di compilazione.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
|
|
|
|
|
#5 | |
|
Bannato
Iscritto dal: Mar 2004
Città: Roma
Messaggi: 2688
|
Quote:
Si verrebbe a creare qualche problema che non renderebbe type safe il linguaggio? why? |
|
|
|
|
|
|
#6 | |
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Quote:
In questo modo invece sai che il metodo esiste di sicuro. D'altronde un modo per fare quello che vuoi c'e'. Basta dichiarare l'oggetto come quadrato e non come rettangolo. Perche' mai vorresti usare rettangolo in questo caso?
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
|
|
|
|
|
|
#7 | |
|
Bannato
Iscritto dal: Mar 2004
Città: Roma
Messaggi: 2688
|
Quote:
|
|
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 15:12.




















