Does the passage of objects in Java simulate passage by reference?

Asked

Viewed 1,558 times

8

Everyone says that Java goes by value, including Objects. These are copied and cannot be reassigned to a new object within the method. The problem is that it is possible to change object values within the method. This does not characterize the passing by reference simulation (done with C pointers++)?

Example of Java Code

class Objeto {

    public int valor;

    public Objeto(){

        this.valor = 2;
    }   
}

public class Passagem {

    public static void main(String[] args) {

        Objeto obj = new Objeto();

        System.out.printf("Valor inicial: %d\n", obj.valor);

        metodo(obj);

        System.out.printf("Valor após o método: %d\n", obj.valor);

        metodoNovo(obj);

        System.out.printf("Valor após o método com novo objeto: %d\n", obj.valor);


    }

    // Simula a passagem por referência alterando o valor do objeto
    // mas não o local de memória para qual aponta
    static void metodo(Objeto obj){

        obj.valor = 5;
    }

    //Tenta alterar o objeto em si
    static void metodoNovo(Objeto obj){

        obj = new Objeto();

        obj.valor = 10;         
    }
}

The value of the object can be directly changed by the first method. Already in the second it is not possible to reassign it to a new object, even because the method expects to receive a constant Object - to change an object itself logic says that we would have to have an object to an object.

The example in C++, which behaves in the same way:

Code Example in C++

#include <iostream> 

using std::cout;
using std::endl;

class Objeto{

    public:
        int valor;

        Objeto(){

            this->valor = 2;
        }
};

void funcao(Objeto *obj){

    obj->valor = 5;
}

void funcaoNovo(Objeto *obj){

    obj = new Objeto();

    obj->valor = 10;
}

int main()
{
    Objeto *obj = new Objeto();

    cout << "Valor inicial: " << obj->valor << endl;

    funcao(obj);

    cout << "Após função: " << obj->valor << endl;

    funcaoNovo(obj);

    cout << "Após função que atribui novo objeto: " 
        << obj->valor << endl;
}

The result of the two codes is the same.

It is only possible to change the object itself in the C++ code if the function expects to receive the address of the object itself, as in:

C++ Function Changes Object

void funcaoNovo(Objeto **obj){

    *obj = new Objeto();

    (*obj)->valor = 10;
}

main to the above function

int main()
{
    Objeto *obj = new Objeto();

    cout << "Valor inicial: " << obj->valor << endl;

    funcao(obj);

    cout << "Após função: " << obj->valor << endl;

    funcaoNovo(&obj);

    cout << "Após função que atribui novo objeto: " 
        << obj->valor << endl;
}

Where it will be possible to see that the reallocation has worked.

Based on these comparisons, it is right to say that in Java there is also a simulation of passing by reference? It is not possible to change the object (the location to which it points), but it is possible to change its values because the function (or method) caller directly changes the value of the values in the objects.

Note: In C it is also possible to do the same test. A given function funcao(int *ptr) cannot assign the ptr to a new malloc within it. Only the value can be changed and is considered a passing simulation by reference.

My biggest doubt:

In C++ the object itself does not change the original value.

void funcaoNovo(Objeto *obj){

    Objeto *novo = new Objeto();

    novo->valor = 10;

    obj = novo;
 }

Here the obj is not changed outside the function. In the same way as in Java.

That is, it is not possible to reassign an object either because C++ cannot change the object passed by copy as well. However can change the value contained in the object (here is the simulation), provided you do not try to change the object.

This is called a passing-by-reference simulation, because the calling function modifies the data contained in the Object and not the Object itself. Java has no reference passage (except with arrays), but it would not be correct to say that there is a simulation of this passage in the value to which the object references, ie I get a simulation when referencing the values?

Because I see that both passages are similar - they allow the function to change values outside the scope (including multiple values), without allowing pointer reallocation (Java reference).

  • I don’t know if I understand your editing, but in Java when you want to modify whole objects the common thing is to involve them in a "wrapper" (an object with reference to another object) and share this wrapper with all interested parties. Thus, if one of them changes the object instance, the other will get the new object when accessing the wrapper (provided they do not save a local copy of the object itself). This is more or less similar to the pointer to pointer, only a little more limited.

  • To change a pointer, by allocating it in a new memory space, in C++ (or C) it takes exactly the same thing: a pointer to pointer. A pointer as a function parameter only ensures changing the value that the object (pointer) contains (or to which it points), not the object itself. Similarly a reference in Java, it can change values, to which it points, within the function that will have an effect outside it. As a pointer (analogous to the reference) does.

2 answers

11


"References" and "reference passage" are two different things. Unlike C/C++, in Java every variable that refers to a complex object does so by means of a reference, not a pointer and not a value type.

C/C++

The three main means of manipulating objects:

Objeto valor; // Um objeto no stack
Objeto* ponteiro = &valor; // Um ponteiro para um objeto no stack ou no heap
Objeto& referencia = valor; // Uma referência para um objeto no stack ou no heap

The way to use them is different: with the value types and references, you can access the members of the object directly, while with the pointers you need to "skid them":

cout << "Valor inicial: " << valor.valor << endl;
cout << "Valor inicial: " << (*ponteiro).valor << endl;
cout << "Valor inicial: " << referencia.valor << endl;

// Atalho para ponteiros (derreferenciação e acesso num único operador)
cout << "Valor inicial: " << ponteiro->valor << endl;

When passing them to a function, the value type causes a copy of the whole object:

void prop1(Objeto arg) {
    arg.valor = 5; // Não afeta o objeto original
}

void redef1(Objeto arg) {
    Objeto novo;
    novo.valor = 10;
    arg = novo; // Não afeta o objeto original
}

The pointer does not copy the object, but as it simply "points" to some object, changing its value will simply make it point to some other object - not affecting the original object:

void prop2(Objeto* arg) {
    arg->valor = 5; // Afeta o objeto original
}

void redef2(Objeto* arg) {
    Objeto* novo = new Objeto();
    novo->valor = 10;
    arg = novo; // Não afeta o objeto original
}

The reference "refers" to the original object. Any change in it will rather affect the original, whether by tampering with its properties, or by reassigning it:

void prop3(Objeto& arg) {
    arg.valor = 50; // Afeta o objeto original
}

void redef3(Objeto& arg) {
    Objeto novo;
    novo.valor = 100;
    arg = novo; // Afeta o objeto original
}

Alternative:

Objeto& referencia2 = *(new Objeto()); // Uma referência para um objeto no heap

void prop4(Objeto& arg) {
    arg.valor = 50; // Afeta o objeto original
}

void redef4(Objeto& arg) {
    Objeto* novo = new Objeto();
    novo->valor = 100;
    arg = *novo; // Afeta o objeto original
}

Example in Ideone. Disclaimer: I have almost no practical experience with C/C++, don’t see the above code as a good way to program in these languages (including, I’m pretty sure there’s at least one memory leak in this code...).

Java

In Java, value types and pointers do not exist, and a reference has some things in common with each of the three:

  • The variable syntax is similar to the value type:

    Objeto referencia;
    
  • The construction with the pointer:

    = new Objeto();
    
  • And the semantics of the end result is similar (but not identical) to the reference:

    Objeto referencia = new Objeto();
    
  • Meanwhile, references are passed by value:

    void prop(Objeto arg) {
        arg.valor = 5; // Afeta o objeto original
    }
    
    void redef(Objeto arg) {
        Objeto novo = new Objeto();
        novo.valor = 10;
        arg = novo; // Não afeta o objeto original
    }
    

What this means, in the end, is that in Java every variable of a complex type contains not the object itself, but a reference (which may or may not be implemented via a pointer - see that answer for more details) for the real object - which only exists in the heap. This reference can be copied to other variables, which also causes them to reference the same object.

Accessing an object by any of its references has the same effect. However, the references themselves are independent of each other. They are copies each other. And tampering with a copy doesn’t change the original. This is why assigning a value to a reference (i.e. making it point to another object) has no effect on the other references to that object - which continue pointing to the original object.

  • "by means of a reference, not a pointer" And when a reference is not a pointer? Only the syntactic sugar of C++ that allows access to the members of the object without the use of C pointer operators. But the information stored in the reference variable is the same. I think your answer confuses this aspect more than it clarifies.

  • @jsbueno "when a reference is not a pointer?" In Java, never! Old JVM implementations (classic and Hotspot) used to use Handles, which were a fixed pointer to another variable pointer (fountain). The recent ones (Sun/Oracle, I don’t know about the many others) or a guy named oop (a class that encapsulates a pointer) or a call narrowOop, a 32-bit integer that needs to be converted to a real pointer before accessing an object (fountain).

  • "References" and "pointers" are two names for the same thing. The difference between both is that C++ has a different syntax to handle both. Whereas by historical/technical limitations the syntax for handling data from a function with a pointer in hand is distinct in C, C++ unifies this syntax, and creates the 'reference''.

  • Try writing a function that accepts two parameters: a pointer to a structure, and a reference to it. make a call to this function by passing a pointer and a reference to the same structure. Now open your program with a debugger and see step by step what are the values effectively passed to the function: from there arrives the same number (a memory address) of 32 or 64 bit as appropriate. Pointer/reference only makes a difference to the syntax required for field manpulation.

  • 1

    @jsbueno Did you ever test this experiment you proposed? I could do it but: 1) I do not have the time, nor am I so eager to prove that I am correct, I have cited several sources that corroborate what I say, if you want to contest them all is your right; 2) my common sense says that he would not behave this way - because if such a function redefines the reference, the external variable will be affected, this is very visible in the ideone example. If everything that the reference passed was the address of the referred, how would the function change the referent? That doesn’t make any sense to me.

  • Some additional (no pun :P) references: ISO C++ References FAQ. Another question on the Soen which makes an extensive comparative analysis of the difference between these two concepts.

Show 1 more comment

1

Almost all object-oriented languages - (if not all) actually pass at all times "by reference" objects - This is -everything that the methods or functions that receive objects as parameters receive is a reference to the original object that is normally represented internally by a memory address (a pointer) - although in some higher-level languages, such as Java and Python, unlike C++, the fact that your method receives a memory address is so transparent to the programmer that it is "invisible".

This is because when you access attributes or methods of the object, in these languages, the syntax is the same - unlike C and C++ where you have to write your code in a way if you have the "value" of the data/object structure or with another notation if you have a "reference". In practice it’s like in those languages you at all times has a reference to the object - even in the place where it is declared and created.

And why then can’t you change the object itself that is referred to? Why the name of the variable that receives the object is in the target function or method - and points to a specific object. If you point the name to another object, the method that called your code has no way of knowing that. On the other hand, if you make changes to the received object, you are actually changing the very same object that whoever called the current method is "seeing".

In fact, outside of C, it doesn’t even make much sense for you to be worrying about whether you’re going through "reference" or "value" most of the time - you need to know that you’re passing an object from one side to the other.

  • Good answer, I only disagree that the distinction is irrelevant outside of C/C++ - C# is a modern language, and it employs this distinction in some cases (e.g..: Structs are a complex object but are a "type-value", and therefore their passage entails a copy; there are also the ref and the out that as far as I know [little; I have no experience with C#] modify the form of passage).

  • Well, I don’t know C#, either, but it doesn’t completely detach from C and C++ (nor name) - if it insists on making memory addresses less transparent, as you put it, it’s more a reflection of that heritage. (which I do not say may not bring about performance gains in some specific constructions, but the expense of the ease of use of language)

  • Unlike its first statement, in Java, C#, Delphi, Ruby, VB.Net... the default is to pass the argument by value, not by reference; and Java doesn’t even support passing arguments by reference (in Java every argument, regardless of its type, is always passed by value). It should be borne in mind that "types by value and types by reference" is different from "passing by value and passing by reference". The links cited in the @mgibsonbr reply clarify this.

  • Caffé: I don’t know the other languages so well, but Java for sure does not duplicate every object passed by a method - which is the main "side Effect" of "pass by value".

  • I think you are confusing passing by reference with the need to use special operators to access the fields of an object in the called function.

  • 2

    Java does not duplicate objects, but duplicates references. C/C++ also duplicate pointers when passed by value. This is something that I myself did not understand well, until we started these discussions (this question and the two that I Inkei), but now it is quite clear to me: pass a pointer to a function is not passing by reference, but passing by value. Only the pointer which is passed by value, not the thing to which he points. So it doesn’t duplicate the objects themselves. But call 100 functions by passing a pointer, and 100 copies of that pointer will be made.

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.