传递对象

时间:2015-12-23 14:46:57

标签: java

我正在检查是否可以使用canForm方法在此boggle板上形成指定的单词。该板具有graph字段,表示相邻的图块。如果可以形成单词,我会执行DFS并将answer设置为true

我理解为什么下面的代码不起作用:answer是一个原语,它的值在每次递归时被复制,而初始answer(在公共方法中)保持为false。

例如,如果我将boolean answer更改为Set<String> answer = new HashSet<>(),则在递归时将引用传递给集合,最后添加成功形成的单词并最终测试空白,它可以正常工作。

但是,如果我只是声明Boolean answer = new Boolean(false)并传递此容器,为什么它不起作用?它可以直接传递对象的引用,但它会在赋值answer = true时神秘地更改引用(通过调试器看到),并且不会重置初始answer。我不明白。

public boolean canForm(String word) {

    boolean answer = false;
    int n = M * N;
    char initial = word.charAt(0);

    // for each tile that is the first letter of word
    for (int u = 0; u < n; u++) {
        char c = getLetter(u / N, u % N);
        if (c == initial) {
            boolean[] marked = new boolean[n];
            marked[u] = true;
            canForm(u, word, 1, marked, answer);
        }
    }

    return !answer;
}

private void canForm(int u, String word, int d, boolean[] marked, boolean answer) {

    if (word.length() == d) {
        answer = true;
        return;
    }

    for (int v : graph.adj(u)) {
        char c = getLetter(v / N, v % N);
        if (c == word.charAt(d) && !marked[v]) {
            marked[v] = true;
            canForm(v, word, d + 1, marked, answer);
        }
    }
}

1 个答案:

答案 0 :(得分:4)

啊,你正在使用Java。这非常重要。

Java完全是一种价值传递语言。因此,当你调用canForm(int,String,int,boolean [],boolean)时,你做了两件事:

  • 您正在canForm方法范围内创建5个新变量
  • 您正在使用来自呼叫站点
  • 的值初始化它们

更改您创建的新变量的值不会对呼叫站点上这些变量的值产生任何影响。当方法调用结束时,您所做的任何重新分配都将丢失,并且不会对呼叫站点的值产生任何影响。

但是,对于数组或对象,传递的“值”实际上是对对象的引用。这可能有点令人困惑,但它就像调用者和方法每个人都拥有自己的共享邮箱个人密钥。要么丢失密钥,要么将密钥替换为不同的邮箱,而不影响对方访问邮箱的能力。

因此,该方法可以在不改变调用者引用的情况下更改引用(marked = new boolean[])的值。但是,如果方法在引用的结构(marked[0] = false)内更改内容,则调用者将看到该内容。这就像打开共享邮箱的方法并更改了里面的邮件一样。无论您使用哪个键打开它,您都会看到相同的更改状态。

一个很好的分析:http://javadude.com/articles/passbyvalue.htm

一般来说:

  • 如果要从方法返回值,则应该是方法的返回值。这使代码更容易理解,不太可能产生副作用,并且可能使编译器更容易优化。
  • 如果要返回两个值,请创建一个对象来保存它们(一个叫做“Tuple”的通用类型安全容器可用于许多语言和框架)。
  • 如果你真的需要在函数调用之间移动状态 - 而你通常不需要 - 用一个对象包装它以获得等效的传递引用语义。这基本上是您在将结果添加到共享集时所执行的操作。请注意:带有副作用的编程,无论是共享对象还是全局状态,都是缺陷的。当你传递一个可变对象时,很难保持所有潜在的变化机制。如果你可以通过返回值专门做一份工作,你应该尝试这样做。有些人可能称之为“功能性[编程]风格。”
  • 当你给两个具有相同名称的不同意图的方法时,它会为读者和编译器的某些情况造成混淆。请明确点。我们没有人物用尽的危险。
  • 最后,您可能想要了解尾递归。由于这个循环,我相信这个实现可能是一个等待发生的堆栈溢出 - 只需给它一个字符串,这个字符串比你的堆栈更深。