3

我有时(实际上,经常)发现自己使用单元素数组从方法返回多个值。像这样的东西:

public static int foo(int param1, int param2[], String param3[])
{
    // method body
    ....
    // set return values
    param2[0] = <some value>;
    param3[0] = <some value>;

    return <some value>;
}

这是一个不好的做法吗?(似乎是因为我的一些朋友说他们不知道它在 2 秒内做了什么!)但我首先使用它的原因是因为它看起来最接近pass-by-referenceC++ 中的已知内容。而且这种做法并没有被劝阻C++,所以......

但是,如果这真的是一种错误的做事方式,那么知道如何以干净的方式重写它吗?

谢谢

4

7 回答 7

6

创建一个包含要返回的数据的对象。

然后您可以返回该对象的一个​​实例。

class FooData {
    private int someInt;
    private int anotherInt;
    private String someString;

    public FooData(int a, int b, String c) {
        someInt = a;
        anotherInt = b;
        someString = c;
    }
}

public FooData foo() {
    // do stuff
    FooData fd = new FooData(blah, blahh, blahhh);
    return fd;
}
于 2013-02-11T20:21:56.253 回答
5

虽然我同意这里的普遍观点,即出于这样的目的使用数组是不好的做法,但我想补充几点。您确定“通过引用传递”真的是您首先需要的吗?许多人说你的代码风格不好,但现在让我告诉你为什么这是恕我直言。“通过引用传递”主要是“副作用编程”的同义词,这是您一直想要避免的事情。它使代码更难调试和理解,并且在多线程环境中,这种态度的不良影响确实会给你带来沉重的打击。要在 Java 中编写可伸缩和线程安全的代码,您应该尽可能使对象“只读”,即理想情况下,您同时创建一个对象并对其进行初始化,然后在整个应用程序中以这种不可修改的状态使用它. 对状态的逻辑更改几乎总是可以被认为是新状态的“创建”,即创建一个初始化为然后需要的状态的新实例。许多现代脚本语言只允许您以这种方式工作,它使事情更容易理解。与 C++ 相比,Java 在分配和释放短期对象方面效率更高,因此这里其他人的建议实际上并没有错:创建一个特殊类的实例来保存函数结果,只是为了达到目的的返回结果。即使您在循环中执行此操作,JVM 也将足够智能以有效地处理该问题。Java 只会在需要时从操作系统中以非常大的块分配内存,并且会在内部处理对象创建和释放,而无需像 C/C++ 这样的语言所涉及的开销。"

编辑:我建议你在这个论坛或网络上搜索“副作用”、“函数式编程”或“不变性”等术语。这很可能会为您的问题打开一个新的视角。

于 2013-02-11T21:12:10.367 回答
4

我认为使用作为方法参数的单元素数组“返回”值是不好的做法。

这是关于这个主题的另一个 SO 问题。简而言之,它对可读性非常不利。

有一个简单的解决方法:将您希望返回的所有值包装在专门为此目的定义的类中,并返回该类的实例。

return new ValueHolder(someValue1, someValue2, someValue3);
于 2013-02-11T20:22:19.083 回答
3

这不是很惯用的java。通常有更好的软件设计方法。

你真正用“单元素数组”做的是创建一个可变对象(因为 String 是不可变的,像 int 这样的原语也是不可变的)并通过引用传递它。修改这个可变对象称为该方法的“副作用”。一般来说,你应该尽量减少可变性(Effective Java Item 15)并且你的方法应该没有副作用。这里有几种方法。

1. 将方法拆分为两个(或三个)都采用相同参数的方法:

public static int foo1(int param1)
{
    // method body
    ....
    return <some value>;
}

同样,您可能有

public static int foo2(int param1) { ... }

public static String foo3(int param1) { ... }.

2. 返回一个复合对象。

public Container {
    private final int originalReturn;
    private final int param2;
    private final String param3;

    public Container(int originalReturn, int param2, String param3) {
        this.originalReturn = originalReturn;
        this.param2 = param2;
        this.param3 = param3;
    }

    // getters
}

public static Container foo(int param1, int param2[], String param3[])
{
    // method body
    ....
    // set return values
    return new Container(<some value>, <some value>, <some value>);
}
于 2013-02-11T20:46:25.757 回答
2

如果值不相关,这确实是不好的做法。这通常是一个指示符,您可以将该函数拆分为两个,每个返回一个值。

编辑:

我假设您要返回在数组中的方法中计算的两个值。不是这样吗?

例如

public int[] getStatistics(int[] nums)
{
    //code

    int[] returns = new int[2];
    returns[0] = mean;
    returns[1] = mode;

    return returns;
}

上述函数可以拆分为getMean()getMode()

于 2013-02-11T20:20:44.953 回答
1

通过引用传递变量允许函数“合法地”改变它们的值。请参阅这篇文章以消除在 Java 中什么时候可以实现这一点的困惑,什么时候不可以......

于 2013-02-11T20:24:21.187 回答
1

如果值具有不同的类型和不同的实体(例如名称和地址等),这是不好的做法。创建具有相同数据类型的数组(例如地址列表)很好。

于 2013-02-11T20:55:51.590 回答