View Full Version : [Questione filosofica] In java esiste il passaggio per riferimento?
Continuazione della discussione iniziata in questo thread:
http://www.hwupgrade.it/forum/showthread.php?t=1363275
dal post numero 12.
____________________________
Riassumo:
In molti (tutti?) libri di java, in documentazione on line, eccetera è scritto
che in java gli oggetti vengono passati per riferimento (in invocazioni locali).
Per alcuni invece - vedi il sito
http://www.yoda.arachsys.com/java/passing.html
insieme a molti altri che potrete trovare con google -
java supporta solamente il passaggio per valore.
Come si evince dall'altro thread la mia opinione (?), per ora, è
risposta = "SI";
Non è finale, in futuro potrei editare la risposta sopra.
In pratica io sono convinto che le ragioni esposte
in particolare nel sito sopra non stiano in piedi,
per il motivo che - vedi il primo esempio citato nel sito -
se io assegno alla referenza (parametro formale) l'indirizzo di
un altro oggetto non ha più senso chiedersi se tale oggetto è stato
passato per valore o per riferimento, in quanto si è solo modificato il
valore del riferimento - che è != dall'oggetto, passato per riferimento.
Altra idea: per distinguere il passaggio per riferimento da quello per valore
considero la "trasparenza" o meno del riferimento -
ad esempio il C supporta solo passaggio per valore, in quanto dicendo
*x
uso esplicitamente il puntatore per accedere alla variabile.
Contorto, eh?
Aspetto altri spunti di discussione...
;)
subvertigo
22-12-2006, 23:18
Il discorso è semplicissimo.
In java ci sono i tipi primitivi (int, float,..) ed i tipi "classe", ovvero riferimento ad oggetti.
Il passaggio di parametri viene fatto sempre per valore, ovvero viene sempre copiato il valore del tipo primitivo o del riferimento.
Le variabili dichiarate come classe sono tutte "puntatori" ad istanze di oggetti, quindi ovviamente nel passaggio l'istanza non viene duplicata...
Perciò la situazione di passaggio di tipi classe è molto simile nel C al passaggio tramite puntatore.
Guarda ... poco tempo fa c'è stato un thread che parlava del passaggio di parametri in C/C++. In quel thread avevo postato anche io dicendo che in "C" il passaggio di parametri può avvenire per valore o per riferimento. Affermazione che ritengo tuttora valida, nonostante in quel thread diverse altre persone sostenessero che in "C" esiste solo il passaggio per valore. Poi non avevo più postato in quel 3d ... perché non volevo innescare chissà quale flame o cose del genere. Insomma sono uno a cui non piace scocciare o rompere le scatole alla gente.
Comunque si può fare un paragone tra C e Java, per capire il perché ritengo che in "C" ci sia il passaggio per riferimento mentre in Java no.
In Java esiste solo ed esclusivamente il passaggio per valore. Se ho una variabile primitiva (int, char ...) e la passo ad un altro metodo, passo una copia del valore. Allo stesso modo, se ho una variabile di tipo reference che fa riferimento ad un oggetto, passo al metodo una copia del reference, cioè una copia del valore contenuto nel reference.
Adesso viene il bello: se nel 'metodoA' ho una variabile di un qualunque tipo (primitivo o reference), secondo voi posso passarla in un qualche modo al 'metodoB' in modo tale che esso possa alterare il valore della variabile nel metodoA??? No, non è possibile. Per questo Java ha solo il passaggio per valore.
Invece in "C" è diverso. Se nella 'funzioneA' ho una variabile (di un qualunque tipo), posso passare alla 'funzioneB' un riferimento alla variabile (il suo indirizzo) in modo tale che il metodoB possa alterare il valore della variabile.
Ecco perché il "C" ha passaggio per valore e per riferimento mentre Java ha solo il passaggio per valore.
Tutto questo IMHO ... ora non massacratemi come nell'altro thread ... :D
^TiGeRShArK^
23-12-2006, 10:55
bhè...
la risposta nonostante le tua opinione è cmq "NO".
E questo non perchè lo dico io ma perchè l'ha detto quello ke vienie considerato il padre di java.
Come ho già detto ho partecipato (o meglio... ho lurkato :asd: ) un bel pò di tempo fa sul forum ufficiale della sun a una discussione su questo argomento.
Lì furono postati diversi articoli e se non sbaglio ache riferimenti ad un libro scritto dal suddetto (di cui ora mi sfugge il nome :p).
Cmq anke senza il "riconoscimento ufficiale" di cui sopra la risposta è cmq NO :D
Hai provato a utilizzare il codice ke ho postato nell'altra discussione?
Se in java il passaggio fosse per riferimento allora non avresti alcun problema a generare una funzione void swap(obj1, obj2) come puoi fare in C utilizzando il passaggio per riferimento...
Però in java, come hai visto dall'esecuzione del codice ke ho postato (e ke ovviamente devi integrare in una classe Point :p) lo swap non funziona.
Questo perchè tu non stai lavorando sulle reference originali degli oggetti ma solo su copie di esse.
Dunque anke il comportamento a livello di codice è completamente diverso rispetto al classico passaggio x riferimento, tanto x non dare adito ad alcun dubbio ;)
^TiGeRShArK^
23-12-2006, 11:00
Ho appena scoperto ke sul forum della sun hanno cancellato il thread aperto da DeFi in cui si parlava di questo :muro:
No.
Ammazza quanto sei stringato oggi... :D
Comunque, ritornando al problema...
Voi dite: "i riferimenti vengono passati per valore"
E io dico: fisicamente il passaggio per valore è l'unico possibile
- anche i puntatori sono valori -
e allora o il passaggio per riferimento non esiste oppure
fatemi un esempio di vero passaggio per riferimento in cui i puntatori
o referenze o quant'altro non siano passati per valore.
Il senso del discorso che facevo prima è che il sito sopra,
nello specifico nel primo esempio, "gioca sporco" perché
modifica i puntatori tramite assegnamento - non per nulla
per confrontare 2 oggetti non si usa == o !=, bensì il metodo
equals, perché l'oggetto non è il suo puntatore, ma ciò che è puntato
da esso, su cui si opera *esclusivamente* con metodi, o con
assegnamenti ai suoi *campi*.
Il fatto che in java non funzioni una swap tra primitivi o tra riferimenti
è dovuto al fatto che questi sono appunto valori.
Ma gli oggetti, quelli "veri", sono un'altra cosa, e si potrebbe pure
fare uno swap, se i metodi dell'oggetto lo consentono.
[...]
Il fatto che in java non funzioni una swap tra primitivi o tra riferimenti
è dovuto al fatto che questi sono appunto valori.
Ma gli oggetti, quelli "veri", sono un'altra cosa, e si potrebbe pure
fare uno swap, se i metodi dell'oggetto lo consentono.
che intendi per "quelli veri"?
Quelli puntati dalle reference, e non le reference stesse, che sono alias
per oggetti, che possono essere riassegnate eccetera.
Un altro articolo che sostiene l'assenza del passaggio per rif. in java è questo:
http://www-128.ibm.com/developerworks/java/library/j-passbyval/
A un certo punto si confronta l'approccio C++ con quello Java,
nei listing 6 e 7.
Quel confronto funziona semplicemente perché le reference in C++ sono
costanti (vero? mi viene ora il dubbio), e quindi non si può fare il
giochetto - usato ad esempio nel primo sito citato - di scambiare le reference.
Lo stesso effetto si può ottenere in java con il passaggio
a volte detto "per costante":
void tricky(final Point arg1, final Point arg2) { ... }
Ancora una cosetta, poi spengo perché devo uscire...
Vorrei cercare di far capire una cosa un po' astratta.
Consideriamo la seguente funzione C++:
void Func(int& a) { a++; }
Ora dichiaro un "oggetto" intero - passatemi il termine:
int n = 10;
Pensate al nome 'n' come a un riferimento all'oggetto '10'
La funzione:
Func(n);
accede all'oggetto '10' mediante il riferimento 'a', lo modifica
(pensate all'operatore ++ come a un metodo),
e questa modifica è visibile all'esterno della funzione.
Ecco, io non vedo differenza tra questo approccio e quello di java per
gli oggetti locali.
Non sto dicendo di aver ragione, solo che non riesco a convincermi,
abbiate pazienza... :ciapet:
Mi siete testimoni, io ho provato ad essere sintetico :D.
Credo che qui non sia in discussione se Java e C possiedano o no il "passaggio per riferimento", cosa che è per entrambi, non simili ma identici sotto questo profilo, preclusa dalle specifiche dei rispettivi linguaggi.
Ogni libro che reciti il fatto che in Java qualcosa sia passato per riferimento è evidentemente scritto da chi non abbia mai letto o abbia mal interpretato le specifiche del linguaggio.
Da notare che la svista ha un autorevole precursore. Fu infatti un documento di Sun Microsystem, rilasciato insieme alla piattaforma Java 1.1, il primo a dire che esisteva una diversità tra il passaggio di una variabile riferimento e quello di una variabile di tipo primitivo. Affermazione fortemente smentita da Gosling, Arnold e Holmes ed evidentemente contraddetta nelle specifiche del linguaggio (si veda ad esempio 8.4.1, "Quando il metodo o il costruttore è invocato il valore delle espressioni, argomenti attuali [dell'invocazione] è usato per inizializzare delle variabili parametro di nuova creazione" o 15.12.4.5, dove al frame di attivazione sono attribuiti "i valori degli argomenti")
Credo invece che la questione riguardi la definizione di "passaggio di una variabile ad una funzione".
La definizione consueta di passaggio di una variabile è semplicissima è dice che passaggio della variabile v alla funzione f significa uso di v come parametro di f.
Quando andbin dice che in C se ho una variabile v posso passare alla funzione f l'indirizzo di quella variabile e ottenere il passaggio per riferimento assume una diversa definizione di "passaggio di una variabile ad una funzione". Non è v ad essere usata come argomento di f ma è una diversa variabile v', correlata a v dal contenere v' l'indirizzo di v.
Io penso che questa diversa definizione di "passaggio di una variabile ad una funzione" sia non già "l'uso di v come argomento di f" ma la "applicazione di f al valore di v", a prescindere dal fatto che v sia o non sia usata come argomento di f.
Per quanti pensino che ridefinire un concetto sia errato ricordo, nel modo più cordiale possibile, che ogni teoria si appoggia ad una o più di questi artifici, tanto che essi sono stati a loro volta "nominati". Si chiamano definizioni speculative di Lesniewsky. Per i masochisti, la nuova definizione che qui stiamo creando non è applicabile allo stesso ambito della vecchia perchè nella teoria (T) della vecchia definizioen (D) la nuova definizione (D') diventa creativa. Cioè data una proposizione S indimostrabile in T (che il passaggio della tal variabile v avvenga per riferimento) esiste una proposizione S' risultante dall'applicazione di D', giudicata equivalente ad S che risulta stavolta dimostrabile in T.
Cioè per la vecchia definizione, una teoria dava una certa proposizione come falsa (che il passaggio di v avvenga per riferimento). Applicando la nuova definizione quella proposizione essa diventa vera (il passaggio di v è per riferimento). Non funziona, si dice che la definizione in T è creativa e quindi viola il secondo limite di Lesniewsky (la non creatività, l'altro è la sostituibilità che mi pare rispettata)
Non funziona ma in quella teoria. Potrebbe anche andare benissimo in una diversa teoria. A naso c'è qualcosa che mi suona bene ma non è mia, non posso tentare di giustificarne l'utilità dall'esterno.
^TiGeRShArK^
23-12-2006, 19:22
Quelli puntati dalle reference, e non le reference stesse, che sono alias
per oggetti, che possono essere riassegnate eccetera.
Un altro articolo che sostiene l'assenza del passaggio per rif. in java è questo:
http://www-128.ibm.com/developerworks/java/library/j-passbyval/
A un certo punto si confronta l'approccio C++ con quello Java,
nei listing 6 e 7.
Quel confronto funziona semplicemente perché le reference in C++ sono
costanti (vero? mi viene ora il dubbio), e quindi non si può fare il
giochetto - usato ad esempio nel primo sito citato - di scambiare le reference.
Lo stesso effetto si può ottenere in java con il passaggio
a volte detto "per costante":
void tricky(final Point arg1, final Point arg2) { ... }
:mbe:
ma l'hai provato il codice ke ho postato? :mbe:
no..xkè provandolo ti accorgeresti che non ti farebbe nemmeno compilare perchè stai tentando di modificare una reference ke è dichiarata final e qdi costante.
Quindi come vedi lo swap definito come metodo void non ha modo di funzionare in java per quanto tu ti possa sforzare a trovare un qualsiasi artificio xkè semplicemente il passagio x riferimento in java non esiste ;)
^TiGeRShArK^
23-12-2006, 19:29
Affermazione fortemente smentita da Gosling, Arnold e Holmes
Ecco bravo :D
era Gosling a cui mi riferivo io se non ricordo male :D
Ma, sai, il problema del chi l'ha detto è sempre relativo. Il nome aggiunge autorevolezza il che rende l'affermazione, appunto, autorevole: ma nulla vieta che sia una stupidaggine. Noi dobbiamo - o dovremmo - guardare a come è definito il linguaggio e verificare se il rispetto di quella definizione ci consenta o non ci consenta di introdurre la caratteristica di cui discutiamo. A mio parere nè C nè Java consentono di dire che in essi esista il passaggio per riferimento, secondo la definizione comune di passaggio.
^TiGeRShArK^
23-12-2006, 19:41
Ma, sai, il problema del chi l'ha detto è sempre relativo. Il nome aggiunge autorevolezza il che rende l'affermazione, appunto, autorevole: ma nulla vieta che sia una stupidaggine. Noi dobbiamo - o dovremmo - guardare a come è definito il linguaggio e verificare se il rispetto di quella definizione ci consenta o non ci consenta di introdurre la caratteristica di cui discutiamo. A mio parere nè C nè Java consentono di dire che in essi esista il passaggio per riferimento, secondo la definizione comune di passaggio.
bhè..
io ho citato lui perchè era intervenuto nella discussione ke lurkavo..e xkè essendo stato uno dei padri di java immagino sapesse ciò di cui parlava.
Ma cmq la verifica da codice non ha lasciato adito a dubbi ;)
X il C onestamente non ho idea se si possa dire ke usi il passaggio x riferimento o no xkè nn lo conosco a fondo come java..
so solo ke & dovrebbe passare il riferimento di un oggetto mentre * il puntatore...
e in effetti la funzione swap in c funziona perfettamente utilizzando &...
qdi....ora tocca a me fare la domanda...xkè nemmeno in C non esiste il passaggio x riferimento? :D
xkè nemmeno in C non esiste il passaggio x riferimento? :D
Non esiste per 6.5.2.2 (ISO/IEC9899).
Voi dite: "i riferimenti vengono passati per valore"
E io dico: fisicamente il passaggio per valore è l'unico possibile
- anche i puntatori sono valori -
e allora o il passaggio per riferimento non esiste oppure
fatemi un esempio di vero passaggio per riferimento in cui i puntatori
o referenze o quant'altro non siano passati per valore.
private void WaitForAnswer(ref String obj, int timeout)
{
int index = 0;
while(obj==null & ++index<timeout)
{
Thread.Sleep(1);
}
if(obj==null)
onErrorEvent(this, new ErrorEventArgs("Timeout waiting answer"));
}
questo è un passaggio per riferimento in C# :fagiano:
mi sta venendo un dubbio, qualcuno mi può dare la definizione ufficiale di "passaggio per riferimento"? Non riesco a trovarla.
^TiGeRShArK^
24-12-2006, 01:42
c'è una discusssione di qualche tempo fa in cui le teorie sono state esposte...
ps & in C è un operatore che denota l' l-value della variabile a cui è applicato....
tu lo passi per valore...ottenendo l"effetto" di un passaggio per riferimento...
mi fermo qui... il resto è su quella discussione :D
link alla discussione :O
:D
mi sta venendo un dubbio, qualcuno mi può dare la definizione ufficiale di "passaggio per riferimento"? Non riesco a trovarla.
la sottigliezza che bisogna cogliere è che nel C i puntatori sono comunque delle variabili che contengono un indirizzo di memoria. perciò passare un puntatore non vuol dire passare argomenti per riferimento, ma passare per valore un indirizzo di memoria. infatti dentro la funzione bisogna sempre usare l'operatore * per recuperare il contenuto dell'indirizzo di memoria. in sostanza cambia il contenuto della variabile, ma è pur sempre passaggio per valore
la sottigliezza che bisogna cogliere è che nel C i puntatori sono comunque delle variabili che contengono un indirizzo di memoria. perciò passare un puntatore non vuol dire passare argomenti per riferimento, ma passare per valore un indirizzo di memoria. infatti dentro la funzione bisogna sempre usare l'operatore * per recuperare il contenuto dell'indirizzo di memoria. in sostanza cambia il contenuto della variabile, ma è pur sempre passaggio per valore
Ah be sì, è chiaro, cioè ovviamente ottieni la stessa cosa in pratica però non è un reale riferimento perché usi la copia dell'indirizzo. Ok, quindi la definizione è quella che sapevo io :asd:
^TiGeRShArK^
24-12-2006, 13:47
http://www.hwupgrade.it/forum/showthread.php?t=1348240&highlight=filepointer
tnx ;)
ora gli do un okkiata :D
Ecco, evidentemente è una questione di definizioni... :confused:
Forse sono abituato troppo bene, e penso troppo "ad alto livello",
ma quando passo un oggetto a una funzione non penso
alla sua reference, ma all'oggetto stesso, che viene passato per
riferimento.
Io non sto dicendo che i discorsi di Gosling e altri non abbiano senso
(swap non funzionanti a parte), il linguaggio l'hanno fatto loro,
sapranno bene come è fatto...
Quello che mi interessava - come si evince dal titolo - era la
"questione filosofica", il fatto se la semantica di passaggio
degli oggetti rispettasse quella per riferimento, e finora
non ho trovato smentite.
Per quanto riguarda i vari codici postati qui e sui vari siti non
"dimostrano" un bel niente da questo punto di vista -
il fatto che una swap tra riferimenti non funzioni non intacca
l'assunto iniziale, perché secondo me l'assegnamento non opera
su oggetti, ma su referenze ad oggetti.
Temo che non riuscirò neanche stavolta a farmi capire, ma io ci provo ;)
ciao a tutti
Mi iscrivo al thread perchè questo argomento mi sembra molto interessante... in particolare perchè mi ha colpito la risposta particolarmente magniloquente ed espressiva di PGI-Bis.
[CUT]
Non sono sicuro di aver capito bene ogni passaggio della discussione, ma proverò ad esporre ugualmente in modo breve le conclusioni che ne ho tratto (per così dire, l'idea che ne ho ricavato) così da farmi correggere se non avessi compreso bene; se invece la mia interpretazione fosse "giusta", potrei affermare di aver contribuito a chiarire la questione... :sofico:
Stando alla documentazione ufficiale del linguaggio Java, citata da PGI-Bis, il passaggio di variabili in Java viene effettuato solo per valore, cioè per copia del contenuto nello stack della funziona chiamata (previa allocazione dello spazio necessario eccetera): per i tipi elementari il passaggio è indolore, per gli oggetti si copia non il valore ma il valore della locazione di memoria dove il valore della variabile effettivamente si trova. Notare che il valore potrebbe essere qualsiasi cosa, de facto si tratterà poi concretamente di un numero che indica una cella di memoria RAM ma, per l'astrattezza della definizione, potrebbe essere qualsiasi cosa grazie alla quale si riesca, "a runtime" a risalire al valore "corrente" della variabile referenziata (per capirci, se operassi su file come blocchi di memoria, potrebbe essere un percorso su di un hard-disk).
Sappiamo che, se alteriamo il valore della variabile passatoci nel primo caso, quello cioè del trasferimento di tipi di dato primitivi, nulla accade alla variabile originaria (quella del chiamante) che risulterà inalterata dall'esecuzione della funzione. Invece, possiamo "accedere" al contenuto della variabile nel secondo caso (grazie all'aiuto del compilatore), e modificarlo in modo che il chiamante possa "vedere" le mofiche da noi effettuate.
Però, se nel secondo caso noi decidessimo di, per esempio, annullare la variabile nel corso della funzione chiamata (in Java, ponendo il valore pari a null), per poi magari riassegnarla ad un altro valore, cioè collegandola ad un'altra zona di memoria (se siamo in Java, ovviamente dello stesso tipo o di un suo erede) ) "a runtime", nulla accadrebbe alla variabile nella funzione chiamante. In Java, avremmo due variabili (una nel chiamante, una nel chiamato) che puntato a due oggetti diversi, cioè, nell'implementazione attuale della virtual machine, due aree di memoria diverse nello heap.
Per questo motivo il passaggio è "per valore": in entrambi i casi, tipo primitivo od oggetto, non c'è una reale possibilità da parte del chiamato di alterare la natura "istanziata" di ciò che è stato passato.
Se ci fosse un "vero" passaggio per riferimento, ci vedremmo passare dalla chiamata di funzione l'unico e il solo riferimento all'oggetto effettivo: se lo cancelliamo, anche l'oggetto originale finirà cancellato; se lo riassegniamo, lo stesso capiterà all'oggetto originale, quello del chiamante.
Spero di non aver scritto castronerie: in caso contrario, qualcuno saprebbe dirmi, se esiste un linguaggio che possiede tra le sue possibilità il "vero passaggio per riferimento" come lo ho delineato io?
Grazie! :cool:
Se ho capito bene quello che vuoi dire, penso il C++
ma anche C# (vedere codice postato prima)
Per quanto riguarda i vari codici postati qui e sui vari siti non
"dimostrano" un bel niente da questo punto di vista -
il fatto che una swap tra riferimenti non funzioni non intacca
l'assunto iniziale, perché secondo me l'assegnamento non opera
su oggetti, ma su referenze ad oggetti.
Temo che non riuscirò neanche stavolta a farmi capire, ma io ci provo ;)
ciao a tutti
beh allora non è proprio passaggio per riferimento.. altrimenti una swap funzionerebbe! potresti chiamarlo con un altro nome (diverso da valore e riferimento.. in effetti spesso si dice per indirizzo), ma visto che tecnicamente è un passaggio per valore è inutile aggiungere ulteriori definizioni.. il tutto è spiegabile dicendo che si passa per valore un indirizzo di memoria
beh allora non è proprio passaggio per riferimento.. altrimenti una swap funzionerebbe! potresti chiamarlo con un altro nome (diverso da valore e riferimento.. in effetti spesso si dice per indirizzo), ma visto che tecnicamente è un passaggio per valore è inutile aggiungere ulteriori definizioni.. il tutto è spiegabile dicendo che si passa per valore un indirizzo di memoria
si passa una copia del valore di un indirizzo di memoria, a quanto ho capito.
^TiGeRShArK^
24-12-2006, 23:42
CUT
Perfetto, ottima spiegazione ;)
cmq voto ank'io x il C++ ora ke mi sono kiarito la confuzione ke avevo in testa tra certe caretteristike del C e del C++ :D
x il C# non ho mai verificato di persona ma vedendo il codice postato da konte direi ad okkio ke vale anke x lui (diciamo ke mi fido x stavolta senza verificare :D)
Ecco, evidentemente è una questione di definizioni...
Esatto. Inoltre, dipende a che "livello" si considera, se il linguaggio in se o anche la sua implementazione. A mio giudizio si puo' tranquillamente dire che anche in Java esiste il passaggio per riferimento, il fatto che a livello implementativo venga fatto tramite copia di puntatori e' un dettaglio.
In effetti il caso del Java e' forse un po' particolare, in quanto legata profondamente alla JVM e quindi ad un modello d'esecuzione specifico.
Per quanto riguarda i vari codici postati qui e sui vari siti non
"dimostrano" un bel niente da questo punto di vista -
il fatto che una swap tra riferimenti non funzioni non intacca
l'assunto iniziale, perché secondo me l'assegnamento non opera
su oggetti, ma su referenze ad oggetti.
Non penso di aver capito, ma concordo :D.
In C++ il famoso swap funziona perche' ci sono degli operatori (impliciti o definiti dall'utente) che provvedono alla copia del contenuto degli oggetti. Quando scrivo la classica implementazione dello swap
template <typename T>
void swap( T& x, T& y )
{
T z = x;
x = y;
y = z;
}
Non dovrebbe essere tecnicamente diverso dal fare (a spanne... non scrivo codice java da qualche anno :p )
void swap( T x, T y )
{
T z = (T)(x.clone());
x.copyFrom( y );
y.copyFrom( z );
}
Se preferisci possiamo imputare l' "impossibilita'" di implementare la swap in java al fatto che gli operatori di assegnamento operano (per gli oggetti) solo sulla reference ( puntatore ) dell'oggetto e non sull'oggetto stesso.
Non dovrebbe essere tecnicamente diverso dal fare (a spanne... non scrivo codice java da qualche anno :p )
void swap( T x, T y )
{
T z = (T)(x.clone());
x.copyFrom( y );
y.copyFrom( z );
}
Se preferisci possiamo imputare l' "impossibilita'" di implementare la swap in java al fatto che gli operatori di assegnamento operano (per gli oggetti) solo sulla reference ( puntatore ) dell'oggetto e non sull'oggetto stesso.
Mmmm... sì, però c'è un problema: non mi risulta che nelle librerie di Java esista il metodo "copyFrom", immagino che dovrebbe copiare ricorsivamente il contenuto di un oggetto in un altro, procedendo "in profondità" fino ad arrivare ai tipi elementari per i quali basta una semplice assegnazione. Lo stesso dicasi per clone(): creare la copia di un oggetto non è un'operazione definita in maniera univoca una volta per tutti i tipi possibili, ma va al contrario definita per il tipo specifico. Voglio dire che utilizzando quanto fornisce Java, a meno di "hackerare" il funzionamento della VM e consentire la copia "fisica" del contenuto di un oggetto in un altro, non è possibile copiare il contenuto di un oggetto in un altro, a meno di implementarsi procedure "apposta", che però dipendono dal tipo dell'oggetto copiato.
Forse si potrebbe ovviare utilizzando la reflection, per creare cioè un metodo "copyFrom" assolutamente generico, però si tratterebbe (sempre che funzioni) di una soluzione "scorretta" al problema, perchè non dimostrerebbe che il problema del passaggio per riferimento in Java non esiste, ma solo che si può implementare la swap in un altro modo, grazie ad un'altra potenzialità del linguaggio - che però nulla ha a che fare con la semantica del passaggio dei parametri.
Insomma, un po' come dimostrare il teorema di Fermat passando per la congettura di Taniyama-Shimura... dimostri il teorema, ma non puoi dire nulla sul fatto che Fermat avesse o meno trovato una soluzione... :sofico:
...
Esattamente quello che intendo io.
In java l'assegnamento tra oggetti non esiste, '=' opera sulle referenze -
per questo le cose appaiono come si è visto in questo thread,
e, se le swap non funzionanti provano qualcosa, provano solo
che le referenze sono passate per valore, cosa ormai appurata.
Quindi non è scorretto dire che in java le referenze vengono
passate per valore, ma altrettanto non è scorretto dire che gli
oggetti vengono passati per riferimento.
Credo che sia un' affermazione per così dire "duale" - e che entrambe
le forme abbiano pari dignità -
ergo il passaggio per riferimento in java esiste, sempre secondo me.
Il fatto che Gosling e altri propendano per la (sola) prima forma non
significa che sia la verità assoluta, quella la sa solo il padreterno.
Questo lo dico non perché io sia migliore di Gosling, ma perché
qualcuno potrebbe dire "L'ha detto Gosling, quindi è vero.".
Mmmm... sì, però c'è un problema: non mi risulta che nelle librerie di Java esista il metodo "copyFrom"
No, il copyFrom l'ho inventato io di sana pianta, a differenza del clone(), era per mostrare che nel caso del C++ non e' nulla piu' che una invocazione di metodo.
, immagino che dovrebbe copiare ricorsivamente il contenuto di un oggetto in un altro, procedendo "in profondità" fino ad arrivare ai tipi elementari per i quali basta una semplice assegnazione.
In linea di massima sì, e infatti in C++ hai bisogno di operare in questo modo per poter fare la swap di cui sopra, perchè in generale il costruttore di copia di default non crea copie degli oggetti referenziati tramite puntatore, ma solo il puntatore stesso.
Lo stesso dicasi per clone(): creare la copia di un oggetto non è un'operazione definita in maniera univoca una volta per tutti i tipi possibili, ma va al contrario definita per il tipo specifico.
Clone mi risulta che esista invece.
Voglio dire che utilizzando quanto fornisce Java, a meno di "hackerare" il funzionamento della VM e consentire la copia "fisica" del contenuto di un oggetto in un altro, non è possibile copiare il contenuto di un oggetto in un altro, a meno di implementarsi procedure "apposta", che però dipendono dal tipo dell'oggetto copiato.
Non è diverso da quanto accade in C++, con la differenza che ci sono degli operatori di copia predefiniti
Forse si potrebbe ovviare utilizzando la reflection, per creare cioè un metodo "copyFrom" assolutamente generico, però si tratterebbe (sempre che funzioni) di una soluzione "scorretta" al problema, perchè non dimostrerebbe che il problema del passaggio per riferimento in Java non esiste, ma solo che si può implementare la swap in un altro modo, grazie ad un'altra potenzialità del linguaggio - che però nulla ha a che fare con la semantica del passaggio dei parametri.
No, ciò dimostra fondamentalmente che la swap non è implementabile in Java perchè siamo in presenza di gerarchie di oggetti e non esistono i riferimenti ai riferimenti, tanto che, se non uso i puntatori, in C++ ho lo stesso identico problema. La swap classica del C++ (la std::swap ad esempio ) non funziona se io passo riferimenti ad oggetti che sono dei tipi derivati.
Prendi l'esempio seguente:
#include <iostream>
using namespace std;
class Base
{
public:
int x;
};
class Derived: public Base
{
public:
int y;
};
class Derived2: public Base
{
public:
int z;
int y;
};
int main( int argc, char **argv )
{
Derived d;
d.x = 1; d.y = 2;
Derived2 d2;
d2.x = 3; d2.y= 4; d2.z = 5;
std::swap( static_cast<Base&>(d), static_cast<Base&>(d2) );
cout << "d: x=" << d.x << " y=" << d.y << endl;
cout << "d2: x=" << d2.x<< " y=" << d2.y << " z=" << d2.z << endl;
}
Che valori vengono stampati dal programma ? Riprendo la definizione di swap esplicitando gli operatori e la classe coinvolta
void swap( Base& x, Base& y )
{
Base z( x );
x.operator=( y ); // Base::operator=
y.operator=( z ); // Base::operator=
}
Ci sono due problemi nell'uso con gerarchie di oggetti: il primo è che costruisci un oggetto intermedio che potrebbe essere del tipo sbagliato ( come nel nostro caso ) o non essere neppure istanziabile ( una classe base astratta ). Inoltre viene chiamato due volte l'operatore di assegnamento della classe base che non sa cosa farne delle informazioni aggiuntive delle classi derivate ( che infatti non copia ).
L'unica soluzione in C++ è utilizzare anche i puntatori:
Base* d = Derived(..);
Base* d2 = Derived2(...);
swap( d, d2 );
La differenza tra C++ e Java sta quindi (nel caso in questione) dall'assenza dei puntatori in Java, o meglio dall'impossibilità di ottenere l'analogo dei puntatori a puntatore (riferimenti a riferimenti, riferimenti a puntatori etc. ).
In altri termini dire se diciamo che in Java non abbiamo il passaggio per riferimento perchè non possiamo implementare la swap, dovremmo dire che non esiste neanche in C++ perchè nemmeno in questo caso è possibile farlo, a meno di non utilizzare una soluzione "scorretta" come dici tu (il ricorso ai puntatori).
scusa ma in C++ l'uso dei puntatori nel caso in esame non deriva da un problema di binding del compilatore?
Non capisco bene la domanda :P , comunque provo a chiarire la mia idea:
Se devo fare la swap tra due elementi ho due alternative: copio le informazioni di uno nell'altro e viceversa, oppure scambio solo due riferimenti ad essi.
Nel primo caso devo per forza avere a che fare con due elementi dello stesso identico tipo, altrimenti introduco tutta una serie di problematiche non banali, come ad esempio dove copiare le informazioni se uno dei due è più grande dell'altro, decidere come copiare in base ai tipi dei due oggetti etc. Ho il vantaggio che il linguaggio mi deve fornire solo i riferimenti degli oggetti.
Nel secondo caso posso scambiare di posto oggetti di qualsiasi tipo e non necessariamente imparentati tra di loro, devo però poter modificare i due riferimenti che vengono passati alla swap, in altri termini devo avere dei riferimenti a quei riferimenti, altrimenti lo scambio avviene solo all'interno della funzione.
Non capisco bene la domanda :P , comunque provo a chiarire la mia idea:
Se devo fare la swap tra due elementi ho due alternative: copio le informazioni di uno nell'altro e viceversa, oppure scambio solo due riferimenti ad essi.
Nel primo caso devo per forza avere a che fare con due elementi dello stesso identico tipo, altrimenti introduco tutta una serie di problematiche non banali, come ad esempio dove copiare le informazioni se uno dei due è più grande dell'altro, decidere come copiare in base ai tipi dei due oggetti etc. Ho il vantaggio che il linguaggio mi deve fornire solo i riferimenti degli oggetti.
Nel secondo caso posso scambiare di posto oggetti di qualsiasi tipo e non necessariamente imparentati tra di loro, devo però poter modificare i due riferimenti che vengono passati alla swap, in altri termini devo avere dei riferimenti a quei riferimenti, altrimenti lo scambio avviene solo all'interno della funzione.
Quello che volevo dire è che fare lo swap fra tipi derivati è un pò come iterare una lista: hai bisogno dei puntatori. Ma questo è un problema di binding dinamico, cioè il binding avviene in esecuzione (correggetemi se sbaglio, sto rimembrando un esame di un anno fa).
Poi c'è anche un'altra cosa, da premettere che non so se vi state riferendo a qualche tipo di swap teorica, ma uno swap fra tipi derivati non mi sembra esattamente uno swap. Due oggetti, che io sappia, si swappano quando sono uguali. Due tipi derivati, oppure padre e figlio, non li puoi swappare, non sono uguali (edit: stessa struttura), ovvio che devi ricorrere a qualche artificio. Poi magari ho capito male io che volevi dire.
Quello che volevo dire è che fare lo swap fra tipi derivati è un pò come iterare una lista: hai bisogno dei puntatori. Ma questo è un problema di binding dinamico, cioè il binding avviene in esecuzione (correggetemi se sbaglio, sto rimembrando un esame di un anno fa).
Esatto, piu' o meno quel che stavo dicendo io prima.
Poi c'è anche un'altra cosa, da premettere che non so se vi state riferendo a qualche tipo di swap teorica, ma uno swap fra tipi derivati non mi sembra esattamente uno swap.
Beh, io a swap do il significato della parola inglese: scambiare di posto :p. Che questo poi avvenga copiandone il contenuto o scambiando solo i riferimenti, a mio giudizio non ne cambia la sostanza.
Due oggetti, che io sappia, si swappano quando sono uguali.
Non necessariamente. Tu immagina un array di oggetti che implementino un generico metodo "creationDate()" ed usare questo per ordinarne in ordine cronologico gli elementi. Le swap che tipicamente andrai ad usare le consideri tali o meno ? Io direi di si', perche' a livello logico lo sono al 100%, a prescindere dal tipo degli oggetti che l'array contiene
Due tipi derivati, oppure padre e figlio, non li puoi swappare, non sono uguali (edit: stessa struttura), ovvio che devi ricorrere a qualche artificio. Poi magari ho capito male io che volevi dire.
Se come swap intendi semplicemente invertire il contenuto della memoria che occupano hai ragione, ma mi sembra una interpretazione un po' riduttiva di swap. In C++ si tende a copiare parecchio il contenuto degli oggetti, per cui puo' sembrare naturale, ma in altri linguaggi si accede agli oggetti solo per riferimento per cui scambiare di posto i riferimenti coincide con scambiare di posto i due oggetti.
Guarda, io non so se è un'interpretazione riduttiva, probabilmente sì, ma è l'unica interpretazione possibile, secondo me, per dare un senso al problema di cui stiamo parlando. Per fare quello che dici tu non basta un passaggio per riferimento...ma non in C++, da nessuna parte e in nessun linguaggio.
Ribadisco, non è che stai dicendo cose sbagliate, è che secondo me sono esempi forzati, perché per swappare padre e figlio è "naturale" che non ti basti un passaggio per riferimento, devi farci delle operazioni su questi due oggetti affinché sia possibile.
Guarda, io non so se è un'interpretazione riduttiva, probabilmente sì, ma è l'unica interpretazione possibile, secondo me, per dare un senso al problema di cui stiamo parlando. Per fare quello che dici tu non basta un passaggio per riferimento...ma non in C++, da nessuna parte e in nessun linguaggio.
Ribadisco, non è che stai dicendo cose sbagliate, è che secondo me sono esempi forzati, perché per swappare padre e figlio è "naturale" che non ti basti un passaggio per riferimento, devi farci delle operazioni su questi due oggetti affinché sia possibile.
Ma io sto appunto dicendo che il problema e' mal posto.
Quello che ho cercato di spiegare nei post precedenti era che
1 - Se si prende per buona la definizione restrittiva di swap, questa funziona anche in Java a patto di limitarsi ad oggetti dello stesso tipo, basta implementare i metodi opportuni e d'altra parte ho mostrato come sia necessario fare la stessa cosa in C++, per cui secondo me non e' una buona discriminante per stabilire se il linguaggio abbia o meno il passaggio per riferimento.
2 - Se si da' per buona quella "estesa" in Java non lo posso fare, mentre per farlo in C++ devo ricorrere ad altri artifici (riferimenti a puntatori, puntatori a puntatori), per cui se ne ricaverebbe che al piu' Java ha il passaggio per riferimento perche' gli mancano i puntatori (a puntatore) :mbe: .
La causa di confusione principale e' probabilmente che in Java l'operatore = non opera direttamente sull'oggetto, ma sul riferimento, ma io lo vedo piu' un problema sulla definizione dell'operatore in se', che obbliga il dover tenere a mente l'implementazione sottostante , piuttosto che un problema sul modo in cui gli argomenti vengono passati.
Purtroppo non riesco a spiegarmi meglio di cosi' :boh:
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.