Przekazywanie parametrów do funkcji – Java kontra C#

Parametry przekazywane do funkcji w Javie są przez wartość, co oznacza że wewnątrz funkcji operujemy na kopii obiektu. I kropka. W przypadku typów prostych faktycznie nie ma problemu:

int mainInt = 60;
System.out.println("mainInt before changes: " + mainInt);
m.changePrimitive(mainInt);
System.out.println("mainInt after changes: " + mainInt);
public void changePrimitive(int localInt){
localInt = 3;
System.out.println("localInt in function: " + localInt);
}

mainInt before changes: 60
localInt in function: 3
mainInt after changes: 60

W przypadku typów obiektowych (referencyjnych) również kopiowany jest obiekt, lecz w praktyce wygląda to nieco inaczej niż w przypadku typów prostych.
Obiekt ogólnie można wyobrazić sobie jako pojemnik, zawierający wartości w swoich polach. „Pola w obiekcie” – co to jednak oznacza? W istocie są to po prostu wskaźniki do obszarów w pamięci (prostych wartości albo całych obiektów). Wewnątrz funkcji tworzona jest „płytka” kopia obiektu – kserujemy więc pudełko (wraz ze wskaźnikami). Wewnątrz metody operujemy na tej kserokopii obiektu, jednak ponieważ skopiowane zostały wskaźniki, zmieniając wartość pól obiektu zmienia się także obiekt oryginalny, pozostający poza funkcją! Mam nadzieję że nieco lepiej widać to na przykładzie:

public class Wrapper {
  public int inside = 0;
}
public void changeWrapper(Wrapper localWrap) {
  localWrap.inside = 17;
  System.out.println("localWrap: " + localWrap.inside);
}
Wrapper mainWrap = new Wrapper();
mainWrap.inside = 5;
System.out.println("mainWrap: " + mainWrap.inside); // 5
m.changeWrapper(mainWrap); // 17
System.out.println("mainWrap: " + mainWrap.inside); // 17!

mainWrap: 5
localWrap: 17
mainWrap: 17

Co natomiast stanie się, jeśli wewnątrz metody podmienimy cały obiekt, redefiniując go poprzez new? Ano, zachowa się wtedy jak typ prosty. Zmiany będą wyłącznie lokalne. Z tego powodu np. w takim przypadku typ String działa jak typ prosty:

public void changeObject(String localString){
  localString += "def";
  System.out.println("localString: " + localString);
}
String mainString = "abc";
System.out.println("mainString before changes: " + mainString);//abc
m.changeObject(mainString);//abcdef
System.out.println("mainString after changes: " + mainString);//abc

Dlaczego? Powodem jest operator +=. Operacja localString+= "def" jest równoważna operacji:

localString = new String(localString + "def");

Wewnątrz funkcji tworzony jest zatem zupełnie nowy obiekt. Wskaźnik wskazujący na wartość mainString pozostaje więc nietknięty.

W Javie nie ma możliwości, by w prosty sposób przekazać do funkcji argument przez referencję. Taka opcja istnieje jednak w C#! Wystarczy dodać magiczne słowo kluczowe ref:

public void changeObjectRef(ref String paramString)
{
  paramString += "def";
}
String stringMain = "abc";
Console.WriteLine("stringMain before changes: " + stringMain);
m.changeObjectRef(ref stringMain);
Console.WriteLine("stringMain after changes: " + stringMain);

stringMain before changes: abc
stringMain before changes: abcdef

Nieźle wytłumaczone zostało to w artykule http://blog.pietowski.com/2010/09/20/przekazywanie-parametrow-w-csharp/trackback/, na przykładzie C#.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *