从方法返回值时,这两个语句有什么区别?

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

我写了一段代码,return是我想要的结果,但我不确定这两种代码变体之间的确切区别是什么:

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

或者:

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

而class我从中调用了一个方法是: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;
    }
}

也就是说,当我使用 addEight() 方法将一个项目添加到 ArrayList 并将其添加到 main 时,我是否必须像这样使用带有赋值运算符的语句:

userRhythm = Notes.addEight(userRhythm);

或者我可以像这样调用方法,它会向列表中添加一个项目。

Notes.addEight(userRhythm);

这两个语句有什么区别,因为它们最终都打印出相同的结果?如果我只使用 Notes.addEight(userRhythm); 而不是 userRhythm = Notes.addEight(userRhythm); Java 如何“知道”一个项目被添加到哪个列表?

假设你是这样写的:

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

在这种情况下,userRhythmuserRhythm2 将引用 相同的 ArrayList。只有一个列表,但有多个引用。

如何解决这个问题?

因为您正在修改参数,所以不需要return。您可以将方法重写为

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

这在语义上更有意义,因为大多数 add 操作根本不 return 任何东西。

两者之间的区别在于,在第一种情况下,您将函数的 return 值分配给 userRythym 变量,而在第二种情况下,您丢弃函数的 return值。

它们的行为相同,因为函数的 return 值为 userRythym,因此赋值(在第一种情况下)只是将变量设置为它已有的值。

您的方法将一个列表作为参数,向该列表添加一个元素,然后 returns 该列表。所以你的两个代码片段在功能上是等价的。

如果方法 returned 另一个 列表而不是它作为参数接收的列表,它们将不等价。例如:

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

在那种情况下,第一个代码片段:

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

将打印 [](空列表),因为它将通过方法 return 编辑的新空列表分配给 userRhythm 并打印它。而第二个片段:

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

将打印 [n8],因为它忽略了由该方法 return 编辑的空列表,并打印该方法已修改的原始列表。

从方法返回一个参数没有多大意义:调用者显然已经有一个对该参数的引用,所以 returning 它是无用且令人困惑的。你最好让你的方法 return void.

ArrayList 是可变的,所以你不应该 return 一个值/重新分配 arraylist 引用(在你现有的代码中)。

如果您的方法实际上 return 是一个 ArrayList (List) 以 ArrayList (List) 作为参数,那么您可以使用returned 值。

示例:

list = someClassInstance.toImmutableList(list); // toImmutableList() returns an immutable representation of list passed as argument 
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);

    }
}

输出:

[n8] -- 如果得到return值

[] - 如果您没有得到 return 值

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

您的代码中只有 1 个列表。它通过引用传递给您的 addEight() 方法,然后 returns 引用。

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

不需要 "know":您正在查看对堆中同一个对象的两个引用。

实参userRhythm是一个对象引用按值传递给addEight , as is the case in Java (see also: Is Java "pass-by-reference" or "pass-by-value"?)

所以:

  • 第一种情况,你是
    • 按值传递对象引用并且
    • 在所述对象上执行 add,它有副作用
  • 在第二种情况下,你是
    • 按值传递对象引用并且
    • 在所述对象上执行 add,它有副作用
    • 将returned对象引用赋值给userRhythm,但是它们是一样的,因为returned对象引用是实参,所以不做a很划算。

如果你有:

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

然后这将产生预期的结果:

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

这个不会:

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

能说说为什么吗?


顺便说一句,事实上,它对 return 任何东西都没有太大用处。 将其设为 void 方法可能会更好。 您正在处理 引用 ,因此您不需要 return 东西,方法的 side effects 就是您想要的。

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.

如果将对象引用传递给方法——并且每个 Java 值要么是原始值(intchar 等),要么是对象引用——然后该方法可以更改所引用对象的任何可修改属性,并且可以通过对该对象的任何引用来观察这些修改。这样的方法也可能 return 对该对象的引用,但这是一个完全不同的问题。始终可以通过对该对象的任何引用访问该对象的当前状态。

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?

区别仅在于,在一种情况下,您将由 Notes.addEight() 编辑的引用 return 分配给 main() 方法的局部变量 userRhythm,而在另一种情况下你没有。因为 Notes.addEight() return 是传递给它的引用的副本,所以这具有将相同的值重新分配给它已经具有的 userRythm 的效果。这没有用,但也无害。

从功能的角度来看,这两种方法的作用完全相同:它们将 "n8" 添加到作为参数传递的列表中。

唯一的区别在于 return 语句:第一个方法不 return 任何东西,而第二个 return 是 对相同内容的引用作为参数传递的 list。虽然这一切看起来很明显,但使用第二个选项可以让您执行以下操作:

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

这是否对您有用,将取决于您的具体问题。

一般来说,有多个引用指向同一个对象是没有意义的。考虑以下因素:

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

在这种情况下,让两个引用指向同一个列表似乎不是很有用,因为这样做就像这样:

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

现在,如果您的 Notes.addEight() 方法是这样实现的,情况会有所不同:

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

如果我们调用这个新的 addEight() 方法,那么情况会有所不同:

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

如您所见,现在 list1list2 是指向不同对象的引用(list1 == list2false),而 list1.equals(list2)true,因为两个列表包含相同顺序的相同元素(它们语义相等)。

然而,list1 == list3true,因为它们实际上指向同一个列表,由于 list3 的声明方式(list3 被分配给 list1 通过赋值运算符 =).