26

由于在 Java 中发送给方法的参数指向调用者方法中的原始数据结构,因此其设计者是否打算让它们用于返回多个值,就像 C 等其他语言中的规范一样?

或者这是对 Java 的一般属性的危险滥用,即变量是指针?

4

9 回答 9

15

很久以前,我与 Ken Arnold(曾经是 Java 团队的成员)进行了一次对话,这可能是在 1996 年的第一次 Java One 会议上。他说他们正在考虑添加多个返回值,这样你就可以写类似:

x, y = foo();

当时和现在推荐的做法是创建一个具有多个数据成员的类并返回它。

基于此,以及从事 Java 工作的人的其他评论,我会说意图是/是您返回一个类的实例,而不是修改传入的参数。

这是常见的做法(正如 C 程序员修改参数的愿望......最终他们通常会看到 Java 的做法。只要把它想象成返回一个结构。:-)

(根据以下评论进行编辑)

我正在读取一个文件并从中生成两个类型为 String 和 int 的数组,从每一行中为两者选择一个元素。我想将它们都返回到任何调用它的函数,以这种方式拆分文件。

我想,如果我理解正确的话,我可能会这样做:

// could go with the Pair idea from another post, but I personally don't like that way
class Line
{
    // would use appropriate names
    private final int intVal;
    private final String stringVal;

    public Line(final int iVal, final String sVal)
    {
        intVal    = iVal;
        stringVal = sVal;
    }

    public int getIntVal()
    {
        return (intVal);
    }

    public String getStringVal()
    {
        return (stringVal);
    }

    // equals/hashCode/etc... as appropriate
}

然后让你的方法是这样的:

public void foo(final File file, final List<Line> lines)
{
    // add to the List.
}

然后这样称呼它:

{
    final List<Line> lines;

    lines = new ArrayList<Line>();
    foo(file, lines);
}
于 2009-04-07T05:40:40.100 回答
7

在我看来,如果我们谈论的是公共方法,您应该创建一个单独的类来表示返回值。当您有单独的课程时:

  • 它用作抽象(即一个Point类而不是两个长数组)
  • 每个字段都有一个名称
  • 可以变得不可变
  • 使 API 的演变更容易(即返回 3 而不是 2 值,更改某些字段的类型等)

我总是选择返回一个新实例,而不是实际修改传入的值。这对我来说似乎更清楚,并且有利于不变性。

另一方面,如果它是一种内部方法,我想可能会使用以下任何一种方法:

  • 一个数组 ( new Object[] { "str", longValue })
  • 一个列表(Arrays.asList(...)返回不可变列表)
  • 对/元组类,例如这个
  • 静态内部类,带有公共字段

不过,我更喜欢最后一个选项,配备合适的构造函数。如果您发现自己从多个地方返回相同的元组,则尤其如此。

于 2009-04-07T08:54:56.467 回答
5

请参阅 1999 年推出的 RFE:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4222792

我不认为打算在 Java 语言中允许它,如果您需要返回多个值,则需要将它们封装在一个对象中。

使用像 Scala 这样的语言,但是您可以返回元组,请参阅:

http://www.artima.com/scalazine/articles/steps.html

您还可以在 Java 中使用泛型返回一对对象,但这就是 AFAIK。

编辑:元组

只是为了补充一些。由于JDK内部缺乏,我以前在项目中实现了Pair。我的实现链接在这里:

http://pbin.oogly.co.uk/listings/viewlistingdetail/5003504425055b47d857490ff73ab9

请注意,这没有哈希码或等号,可能应该添加。

我在对提供元组功能的这个问题进行一些研究时也遇到了这个问题:

http://javatuple.com/

它允许您创建包含其他类型元组的 Pair。

于 2009-04-07T05:18:27.140 回答
5

我真希望Pair<E,F>JDK 中有一个类,主要是因为这个原因。有Map<K,V>.Entry,但是创建一个实例总是很痛苦。

现在我com.google.common.collect.Maps.immutableEntry在需要时使用Pair

于 2009-04-07T06:33:21.843 回答
2

您不能真正返回多个值,但您可以将对象传递给方法并让方法改变这些值。这是完全合法的。请注意,您不能将对象传入并让对象本身成为不同的对象。那是:

private void myFunc(Object a) {
    a = new Object();
}

将导致临时本地更改 的值a,但这不会更改调用者的值,例如,从:

Object test = new Object();
myFunc(test);

myFunc 返回后,您将拥有的Object 而不是新的。

合法的(并且经常不鼓励)是这样的:

private void changeDate(final Date date) {
    date.setTime(1234567890L);
}

我选择Date是有原因的。这是一个人们普遍认为不应该是可变的类。上面的方法Date改变你传递给它的任何对象的内部值。当方法很明显会变异或配置或修改传入的内容时,这种代码是合法的。

注意:通常,据说一个方法应该做以下事情之一:

  • 返回 void 并改变其传入的对象(如Collections.sort()),或
  • 返回一些计算并且根本不改变传入的对象(例如Collections.min()),或者
  • 返回传入对象的“视图”,但不修改传入对象(如Collections.checkedList()Collections.singleton()
  • 变异一个传入的对象并返回它(Collections没有示例,但是StringBuilder.append()是一个很好的示例)。

改变传入对象返回单独返回值的方法通常会做太多事情。

于 2009-04-07T05:16:29.797 回答
0

当然有一些方法可以修改作为参数传入的对象(参见 java.io.Reader.read(byte[] buffer为例),但我还没有看到参数用作返回值的替代方案,特别是对于多个参数。它在技术上可能有效,但它是非标准的。

于 2009-04-07T05:29:12.840 回答
0

通常认为这不是非常好的做法,但在 JDK 中偶尔会出现这种情况。查看 View.getNextVisualPositionFrom() 的 'biasRet' 参数和相关方法,例如:它实际上是一个填充有“额外返回值”的一维数组。

那么为什么要这样做呢?好吧,只是为了让您不必为“偶尔的额外返回值”创建一个额外的类定义。它是凌乱、不雅、糟糕的设计、非面向对象的,等等。而且我们都时不时地这样做...

于 2009-04-07T06:03:21.370 回答
0

一般是 Eddie 所说的,但我想再补充一句:

  • 改变一个传入的对象,并返回一个状态码。这通常应该只用于显式缓冲区的参数,例如 Reader.read(char[] cbuf)。
于 2009-04-07T22:04:10.233 回答
0

我有一个 Result 对象,它通过一系列验证 void 方法作为方法参数级联。这些验证 void 方法中的每一个都会改变结果参数对象以添加验证结果。

但是这是不可能测试的,因为现在我不能存根 void 方法来返回存根值以在 Result 对象中进行验证。

因此,从测试的角度来看,似乎应该倾向于返回一个对象而不是改变方法参数。

于 2009-12-17T20:11:57.437 回答