Transferul de parametri către metode

La invocarea unei metode, este necesar să se transmită de la metoda care invocă la metoda invocată parametrii (argumentele) acesteia. De exemplu, la executarea invocării Math.sin(a), este necesar să se transmită către metoda sin argumentul acesteia, a.
 
În teoria și practica programării se cunosc diferite moduri în car se poate face transmiterea argumentelor către o funcție sau procedură:
   - transmitere prin valoare: de la programul apelant către funcție (procedură) se transmit valorile argumentelor;
   - transmitere prin adresa: de la programul apelant către funcție (procedură) se transmit adresele la care se găsesc în memorie valorile argumentelor;
   - transmitere prin nume: de la programul apelant către funcție (procedură) se transmit numele argumentelor;
   - transmitere prin referință: de la programul apelant la funcție (procedură) se transmit referințe către argumente.

În limbajul Java, transmiterea parametrilor (argumentelor) metodelor se face prin valoare. Aceasta înseamnă că:
    - dacă argumentul aparține unui tip de date primitiv, se transmite chiar valoarea primitivă a argumentului respectiv;
    - dacă argumentul aparține unui tip-referință (este instanță a unei clase), se transmite - de fapt - o referință către un obiect din clasa respectiva sau dintr-o clasa derivată din aceasta.
 
Exemplu
Să considerăm metoda int indexOf(String str, int fromIndex) din clasa String. Primul argument al acestei metode este o referință la un obiect din clasa String, iar al doilea argument este o valoare primitivă de tip int. În limbajul Java, cei doi parametri str si fromIndex sunt considerați variabile locale ale metodei indexOf, iar transmiterea parametrilor este echivalentă cu operația de atribuire. În consecință, dacă se invocă această metodă sub forma indexOf("abc", 3), variabilei-referință str i se atribuie ca valoare referința la sirul "abc" (și nu insuși șirul "abc", care rămâne la locul lui în memoria dinamică), în timp ce variabilei fromIndex de tip int i se atribuie valoarea primitivă 3
Să considerăm acum că într-un program există instrucțiunile:
    int k=7, j;
    String s1="un exemplu de sir", s2="exem";
    j=s1.indexOf(s2,k);
Evaluarea metodei indexOf va decurge astfel: se transmit de la programul apelant către metoda indexOf valorile argumentelor s2 si k. Numai că s2 este o variabila-referință, deci valoarea ei este referința la șirul "exem" și nu însuși acest șir. În schimb, valoarea variabilei k este chiar valoarea primitivă 7. În consecință, în șirul indicat de s1, se va căuta subșirul a cărui referință este s2, începand căutarea de la poziția de indice 7.
În teoria programării, argumentele (parametrii) care apar în declarația unei metode (funcții, proceduri) se numesc parametri formali, iar valorile prin care se substituie acești parametri formali la invocarea metodei respective se numesc parametri efectivi. Astfel, în exemplul de mai sus, str și fromIndex sunt parametri formali ai metodei indexOf, in timp ce "abc", 3, valoarea lui s2, și valoarea lui k sunt parametri efectivi.

Consecința faptului că parametrii metodelor se transmit prin valoare este următoarea: chiar dacă în corpul unei metode se atribuie unui argument formal al acesteia o nouă valoare, această atribuire nu modifică valoarea argumentului efectiv corespunzător din programul apelant. Daca însă, în cazul unui argument-referință, nu se modifica referința însăși, ci valorile câmpurilor obiectului indicat de către aceasta, modificarea respectivă se va transmite și la programul apelant, constituind efectul lateral al metodei respective.
 
Exemplu
În fișierul TestParam.java este dat următorul program, în care se testează un caz de metodă care își modifică parametrii:
 
/* Testarea modului de transmitere a parametrilor catre metode */

/* O clasa oarecare, continand un camp a */
class Proba2 {
  int a;
}

class TestParam {
  /* O metoda in care se modifica valorile propriilor parametri formali
  */
  static void modParam(int k, Proba2 p1, Proba2 p2) {
    System.out.println("La intrarea in modParam k="+k+" p1.a="+p1.a+
      " p2.a="+p2.a);
    k=-111; // S-a modificat valoarea parametrului k de tip int
    p1.a=-222; // S-a modificat valoarea unui camp al obiectului cu 
               // referinta p1, dar nu insasi referinta p1
    p2=new Proba2(); // S-a modificat insasi referinta p2, catre 
                     // o noua instanta a clasei Proba2
    p2.a=-333;       // S-a atribuit valoare campului a al noii 
                     // instante referite prin p2
    System.out.println("In modParam dupa modificarile de parametri:\n"+
      "k="+k+" p1.a="+p1.a+" p2.a="+p2.a);
  }

  /* Metoda principala */
  public static void main(String args[]) {
    // Se declara si se initializeaza variabilele
    int m=123;
    Proba2 pr1=new Proba2(), pr2=new Proba2();
    pr1.a=333; pr2.a=444;
    System.out.println("In main inainte de a invoca modParam:\n"+
      "m="+m+" pr1.a="+pr1.a+" pr2.a="+pr2.a);
    // Se invoca metoda modParam
    modParam(m, pr1, pr2);
    // Se afiseaza valorile parametrilor dupa revenirea din modParam
    System.out.println("In main dupa ce s-a invocat modParam:\n"+
      "m="+m+" pr1.a="+pr1.a+" pr2.a="+pr2.a);
  }
}

Executând acest program, obținem afișate pe ecran următoarele rezultate:
 
In main inainte de a invoca modParam:
m=123 pr1.a=333 pr2.a=444
La intrarea in modparam k=123 p1.a=333 p2.a=444
In modParam dupa modificarile de parametri
k=-111 p1.a=-222 p2.a=-333
In main dupa ce s-a invocat modParam:
m=123 pr1.a=-222 pr2.a=444

Remarcăm că, deși în metoda modParam s-au modificat valorile lui k, p1.a si p2.a, la revenirea din modParam în main valorile lui m și pr2.a (care corespund respectiv lui k și p2.a din modParam) au rămas cele anterioare invocării acestei metode, în timp ce pr1.a (corespunzătoare lui p1.a) s-a modificat. Iată cum se explică cele constatate:
  1/ Parametrul formal k este de tipul primitiv int. În consecință, la invocarea metodei modParam, parametrul efectiv corespunzator se transmite prin valoare, adică se poate considera că variabilei locale k din modParam i s-a atribuit valoarea lui m, respectiv 123. În schimb, valoarea variabilei m din main a rămas nemodificată.
  2/ Parametrul formal p1 este de tip referință la o instanță a clasei Proba2. La invocarea metodei modParam, acestui parametru i s-a atribuit valoarea variabilei referința pr1 din main, adică o referință către obiectul din clasa Proba2 care a fost creat în main și are câmpul a=333. În modParam se modifică valoarea câmpului a din acest obiect. Este deci normal să constatăm că, la revenirea din modParam, valoarea câmpului pr1.a s-a modificat, deoarece pr1.a==pr2.a.
  3/ Parametrul formal p2, la fel ca p1, este o referință la o instanță a clasei Proba2. La intrarea în modParam, lui p2 i se atribuie valoarea parametrului efectiv pr1 din main, adică o referință la obiectul în care campul pr1.a are valoarea 444. În metoda modParam se modifică valoarea parametrului p2, adică se creeaza o nouă instanță a clasei Proba2 și se atibuie lui p2 ca valoare o referință la aceasta nouă instanță. Când se face apoi atribuirea p2.a=-333, se modifică valoarea câmpului a din această nouă instanță, fără a se modifica valoarea câmpului pr2.a al obiectului referit inițial. În consecință, la revenirea în main constatăm că pr2.a a rămas la valoarea anterioară invocarii metodei modParam.

Metode care întorc o referință la un obiect construit în corpul lor

În limbajul Java este posibil ca o metodă să întoarcă o referință la un obiect construit în interiorul ei. Un astfel de exemplu este dat în fișierul TestRef.java, care conține următorul program:
 
/* Testarea unei metode care intoarce un sir */

class TestRef {

  static String metoda1() {
    String str=new String("un sir");
    System.out.println("In metoda1 str="+str);
    return str;
  }

  static void metoda2(String s) {
    String s1=new String("alt sir");
    s=s1;
    System.out.println("In metoda2 s="+s);
  }

  public static void main(String args[]) {
    String sir1=null, sir2=null;
    sir1=metoda1();
    metoda2(sir2);
    System.out.println("In main sir1="+sir1+"\nsir2="+sir2);
  }
}

În metoda1 se creează prin operatorul new un nou șir, iar referința str la acesta este "întoarsă" de această metodă la executarea instrucțiunii return. În metoda2 se creează de asemenea un nou șir, dar referința la acesta se atribuie argumentului s al metodei. La executarea acestui program se obțin următoarele rezultate:
 
In metoda1 str=un sir
In metoda2 s=alt sir
In main sir1=un sir
sir2=null

Se observă că metoda1 a întors corect referința către șirul "un sir" care a fost creat în interiorul ei. În schimb, metoda2 nu a avut ca efect lateral transmiterea catre variabila-referință sir2 din main a referinței către obiectul "alt sir" creat în această metodă deoarece, după cum s-a arătat anterior, transmiterea parametrilor se face prin valoare și deci modificarea în corpul metodei a valorii parametrului formal s nu afectează valoarea parametrului efectiv str2 prin care acesta a fost substituit la invocarea metodei respective.

Metode care au ca argumente și/sau ca valori întoarse referințe la tablouri

În limbajul Java, tablourile sunt obiecte. În consecință, dacă parametrii formali ai unei metode sunt tablouri, numele lor sunt, de fapt, variabile-referință. De exemplu, în signatura metodei main
   main(String args[])
parametrul formal args[] este o referință la un tablou, ale cărui elemente aparțin clasei String (sunt șiruri de caractere). În consecință, tot ce s-a prezentat în secțiunea anterioară cu privire la folosirea ca parametri formali a variabilelor- referință este valabil și pentru tablouri. Pentru exemplificare, considerăm programul următor, din fișierul TestTab.java.
 
/* Metode care au ca parametri si ca valori intoarse referinte
   la tablouri 
*/

class TestTab {

  static int[] alpha(int a[], double b[]) {
    System.out.print("La intrarea in metoda alpha\n Tabloul a: ");
    for(int i=0; i<a.length; i++) System.out.print(a[i]+" ");
    System.out.print("\nTabloul b: ");
    for(int i=0; i<b.length; i++) System.out.print(b[i]+" ");
    /* Se creaza tabloul c si se dau valori elementelor lui */
    int c[]=new int[4];
    for(int i=0; i<c.length; i++) c[i]=2*i+1;
    System.out.println("\nTabloul c alocat si initializat in aceasta "+
      "metoda:");
    for(int i=0; i<c.length; i++) System.out.print(c[i]+" ");
    /* Se modifica valorile primelor doua elemente ale tabloului b */
    b[0]=-777.77; b[1]=-999.99;
    /* Se creaza un nou tablou, iar referinta catre el se atribuie
       parametrului formal a:
    */
    a=new int[3];
    a[0]=1000; a[1]=1001; a[2]=1002;
    System.out.println("Inainte de iesirea din metoda alpha:");
    System.out.print("Tabloul a: ");
    for(int i=0; i<a.length; i++) System.out.print(a[i]+" ");
    System.out.print("\nTabloul b: ");
    for(int i=0; i<b.length; i++) System.out.print(b[i]+" ");
    System.out.println();
    return c;
  }

  public static void main(String args[]) {
    int p[]={10, 11, 12, 13, 14}, q[];
    double w[]={1.1, 1.2, 1.3, 1.4, 1.5, 1.6};
    q=alpha(p,w);
    System.out.println("In main dupa revenirea din alpha:");
    System.out.print("Tabloul p: ");
    for(int i=0; i<p.length; i++) System.out.print(p[i]+" ");
    System.out.print("\nTabloul q: ");
    for(int i=0; i<q.length; i++) System.out.print(q[i]+" ");
    System.out.print("\nTabloul w: ");
    for(int i=0; i<w.length; i++) System.out.print(w[i]+" ");
    System.out.println();
  }
}

Rezultatele afișate la executarea acestui program sunt următoarele:
 
La intrarea in metoda alpha
Tabloul a: 10 11 12 13 14
Tabloul b: 1.1 1.2 1.3 1.4 1.5 1.6
Tabloul c alocat si initializat in aceasta metoda:
1 3 5 7
Inainte de iesirea din metoda alpha:
Tabloul a: 1000 1001 1002
Tabloul b: -777.77 -999.99 1.3 1.4 1.5 1.6
In main dupa revenirea din alpha:
Tabloul p: 10 11 12 13 14
Tabloul q: 1 3 5 7
Tabloul w: -777.77 -999.99 1.3 1.4 1.5 1.6

Urmărind executarea programului, constatăm că:
  - deși parametrului formal a i s-a atribuit în interiorul metodei alpha o referință la alt tablou, nou construit, aceasta nu a afectat tabloul referit în metoda main prin parametrul efectiv corespunzător p;
  - întrucât în metoda alpha s-au modificat valorile elementelor tabloului referit prin parametrul formal b, aceste modificări apar și în metoda main în tabloul referit de parametrul efectiv corespunzatorw (deoarece b și w sunt, de fapt, referințe la același tablou);
  - referința la tabloul c, creat în corpul metodei alpha, a fost intoarsă de această metodă și a fost atribuită în metoda main variabilei referință q; în consecință, c și q sunt referințe la același tablou.

Remarcăm că, la ieșirea din metoda alpha, variabila locală c este eliminată de pe stiva sistemului. Cu toate acestea, tabloul creat în metoda alpha și referit de această variabilă locală nu este eliminat din memorie, deoarece către el indică în continuare variabila-referință q din metoda main.
 



© Copyright 2000 - Severin BUMBARU, Universitatea "Dunărea de Jos" din Galați