简体   繁体   中英

What is the difference between these two statements when a value is returned from a method?

I've written a piece of code that returns the result I want, but I'm not sure what exactly difference between these two variants of code is:

ArrayList<String> userRhythm = new ArrayList<String>();
userRhythm = Notes.addEight(userRhythm);
System.out.println(userRhythm);

Alternatively:

ArrayList<String> userRhythm = new ArrayList<String>();
Notes.addEight(userRhythm);
System.out.println(userRhythm);

And class from which I called a method is: Notes class (Notes.java file)

import java.util.ArrayList;

public class Notes {

    public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
        userRhythm.add("n8");
        return userRhythm;
    }
}

Namelly, when I add an item to ArrayList using addEight() method, and return it to the main, do I have to use statement with assignment operator like this:

userRhythm = Notes.addEight(userRhythm);

Or I could just call the method like this and it will add an item to the list.

Notes.addEight(userRhythm);

What is difference between these two statements, since in the end they both print same result? If I use only Notes.addEight(userRhythm); and not userRhythm = Notes.addEight(userRhythm); how does Java “knows” to which list an item was added?

how does Java “knows” to which list an item was added?

It doesn't need to "know": you are looking at two references to the same single object in the heap.

Actual parameter userRhythm is an object reference passed to addEight by value , as is the case in Java (see also: Is Java "pass-by-reference" or "pass-by-value"? )

So:

  • In the first case, you are
    • Passing an object reference by value and
    • executing add on said object, which has side effects
  • in the second case, you are
    • Passing an object reference by value and
    • executing add on said object, which has side effects
    • Assigning the returned object reference to userRhythm , but they are the same, since the returned object reference is the actual parameter, so it doesn't do a great deal.

If you instead had:

public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
    userRhythm.add("n8");
    return new ArrayList<String>();
}

then this would produce the expected result :

ArrayList<String> userRhythm = new ArrayList<String>();
Notes.addEight(userRhythm);
System.out.println(userRhythm);

This would not :

ArrayList<String> userRhythm = new ArrayList<String>();
userRhythm = Notes.addEight(userRhythm);
System.out.println(userRhythm);

Can you say why?


As a side note, it's not terribly useful to return anything, in fact. You could probably better off by making it a void method. You are dealing with references , hence you don't need to return things, you method's side effects are what you want.

Suppose you had written it like this:

ArrayList<String> userRhythm = new ArrayList<String>();
ArrayList<String> userRhythm2 = Notes.addEight(userRhythm);
System.out.println(userRhythm2);

In that case, userRhythm and userRhythm2 would be references to the same ArrayList . There is only one list, but multiple references to it.

How can you resolve this?

Because you are modifying the parameter, you don't need to return it. You could rewrite your method as

public static void addEight(ArrayList<String> userRhythm) {
    userRhythm.add("n8");
}

This makes more sense semantically, because most add operations don't return anything at all.

Your method takes a list as argument, adds an element to that list, and returns that list. So your two code snippets are functionally equivalent.

They would not be equivalent if the method returned another list than the one it receives as argument. For example:

public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
    userRhythm.add("n8");
    return new ArrayList<String>();;
}

In that case, the first code snippet:

ArrayList<String> userRhythm = new ArrayList<String>();
userRhythm = Notes.addEight(userRhythm);
System.out.println(userRhythm);

would print [] (empty list), since it assigns the new, empty list returned by the method to userRhythm and prints it. Whereas the second snippet:

ArrayList<String> userRhythm = new ArrayList<String>();
Notes.addEight(userRhythm);
System.out.println(userRhythm);

would print [n8] , since it ignores the empty list returned by the method, and prints the original list, that the method has modified.

Returning an argument from a method doesn't make much sense: the caller obviously already has a reference to that argument, so returning it is useless and confusing. You'd better make your method return void .

The difference between the two is that in the first case, you are assigning the return value of the function to the userRythym variable, while in the second case, you discard the function's return value.

They act the same because the return value of the function is userRythym , so the assignment (in the first case) is just setting the variable to the value it already has.

ArrayList is mutable, so you shouldn't return a value / reassign the arraylist reference (in your existing code).

If your method actually returns an ArrayList ( List ) taking an ArrayList ( List ) as an argument, then you could use the returned value.

example :

list = someClassInstance.toImmutableList(list); // toImmutableList() returns an immutable representation of list passed as argument 

From a functional point of view, both methods do exactly the same: they add "n8" to the list passed as an argument.

The only difference is in the return statement: the first method does not return anything, while the second one returns a reference to the same list that was passed as an argument. While all this seems quite obvious, using the second option would allow you to do the following:

ArrayList<String> list = new ArrayList<>();
boolean hasNote = Notes.addEight(list).contains("n8"); // true

Whether this is useful to you or not, it will depend on your specific problem.

In general, it doesn't make sense to have more than one reference pointing to the same object. Consider the following:

ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = Notes.addEight(list1);

System.out.println("list1 == list2 ? " + (list1 == list2)); // true, they point to 
                                                            // the same list
System.out.println("list1.equals(list2) ? " + (list1.equals(list2))); // true, since they 
                                                                      // not only contain 
                                                                      // the same 
                                                                      // elements, but
                                                                      // also point to the
                                                                      // same list

In this scenario, it doesn't seem to be very useful to have two references pointing to the same list, since it would be as doing this:

ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = Notes.addEight(list1);
ArrayList<String> list3 = list1;

System.out.println("list1 == list2 ? " + (list1 == list2)); // true
System.out.println("list1.equals(list2) ? " + (list1.equals(list2))); // true
System.out.println("list1 == list3 ? " + (list1 == list3)); // true
System.out.println("list1.equals(list3) ? " + (list1.equals(list2))); // true

Now, it would be different if your Notes.addEight() method was implemented this way:

public static ArrayList<String> addEight(ArrayList<String> list) {
    list.add("n8");
    return new ArrayList<>(list); // NOTE THIS! A new ArrayList is being returned
}

If we invoked this new addEight() method, then things would be different:

ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = Test.addEight(list1);
ArrayList<String> list3 = list1;

System.out.println("list1 == list2 ? " + (list1 == list2)); // false
System.out.println("list1.equals(list2) ? " + (list1.equals(list2))); // true
System.out.println("list1 == list3 ? " + (list1 == list3)); // true
System.out.println("list1.equals(list3) ? " + (list1.equals(list3))); true

As you see, now list1 and list2 are references that point to different objects ( list1 == list2 is false ), while list1.equals(list2) is true , since both lists contain the same elements in the same order (they are semantically equal).

However, list1 == list3 is true , since they actually point to the same list, due to the way list3 was declared ( list3 was assigned to list1 by means of the assignment operator = ).

See the difference between these two implementations by creating new object in addEight method

import java.util.ArrayList;

public class Notes {

    public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
        userRhythm = new ArrayList<String>();
        userRhythm.add("n8");
        return userRhythm;
    }
    public static void main(String args[]){
    ArrayList<String> userRhythm = new ArrayList<String>();
    userRhythm = Notes.addEight(userRhythm);
    System.out.println(userRhythm);


    ArrayList<String> userRhythm1 = new ArrayList<String>();
    Notes.addEight(userRhythm1);
    System.out.println(userRhythm1);

    }
}

Output:

[n8] -- If you get return value

[] - If you don't get the return value

how does Java “knows” to which list an item was added?

There is only 1 list in your code. It is passed by reference to your addEight() method, which then returns the reference.

Namelly, when I add an item to ArrayList using addEight() method, and return it to the main, do I have to use statement with assignment operator [...] Or I could just call the method like this and it will add an item to the list.

If you pass an object reference to a method -- and every Java value is either a primitive ( int , char , etc.) or an object reference -- then the method can alter any modifiable properties of the referred-to object, and those modifications can be observed via any reference to the object. Such a method may return a reference to that object as well, but that's an altogether separate issue. The current state of an object is always accessible via any reference to that object.

What is difference between these two statements, since in the end they both print same result? If I use only Notes.addEight(userRhythm); and not userRhythm = Notes.addEight(userRhythm); how does Java “knows” to which list an item was added?

The difference is simply that in one case you assign the reference returned by Notes.addEight() to the main() method's local variable userRhythm , and in the other case you don't. Because Notes.addEight() returns a copy of the reference passed to it, this has the effect of re-assigning the same value to userRythm that it already had. That's not useful, but not harmful, either.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM