Java方法参数是否应该用于返回多个值?
由于发送到Java中的方法的参数指向调用方法中的原始数据结构,因此设计者是否打算将它们用于返回多个值,这是其他语言(例如C)的常规吗?
或者这是对Java的一般属性的危险误用,变量是指针吗?
很久以前我和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 lines) { // add to the List. }
然后像这样调用它:
{ final List lines; lines = new ArrayList (); foo(file, lines); }
在我看来,如果我们谈论的是公共方法,你应该创建一个代表返回值的单独类。 当你有一个单独的类:
- 它作为一个抽象(即一个
Point
类而不是两个longs的数组) - 每个字段都有一个名称
- 可以变得一成不变
- 使API的演化更容易(即返回3而不是2个值,改变某些字段的类型等)
我总是选择返回一个新实例,而不是实际修改传入的值。对我来说似乎更清楚,并且支持不变性。
另一方面,如果它是一个内部方法,我想可能会使用以下任何一种方法:
- 一个数组(
new Object[] { "str", longValue }
) - 列表(
Arrays.asList(...)
返回不可变列表) - pair / tuple类,比如这个
- 静态内部类,带有公共字段
不过,我更喜欢最后一个选项,配备合适的构造函数。 如果您发现自己从多个地方返回相同的元组,则尤其如此。
我希望JDK中有一个Pair
类,主要是出于这个原因。 有Map
,但是创建一个实例总是很痛苦。
现在我在需要一Pair
时使用com.google.common.collect.Maps.immutableEntry
见1999年推出的RFE:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4222792
如果你需要返回多个值来将它们封装在一个对象中,我不认为它的目的是在Java语言中允许它。
使用像Scala这样的语言但是你可以返回元组,请参阅:
http://www.artima.com/scalazine/articles/steps.html
您也可以使用Java中的generics来返回一对对象,但这就是AFAIK。
编辑:元组
只是为此添加更多内容。 我之前在项目中实现了一对,因为JDK内部缺乏。 链接到我的实现在这里:
http://pbin.oogly.co.uk/listings/viewlistingdetail/5003504425055b47d857490ff73ab9
注意,没有哈希码或等于此,应该添加。
我也遇到了这个问题,同时对这些提供元组function的问题进行了一些研究:
它允许您创建包括其他类型的元组。
您无法真正返回多个值,但您可以将对象传递给方法并让方法改变这些值。 这完全合法。 请注意,您无法传入对象并使对象本身成为不同的对象。 那是:
private void myFunc(Object a) { a = new Object(); }
将导致临时和本地更改a的值,但这不会更改调用者的值,例如,来自:
Object test = new Object(); myFunc(test);
myFunc返回后,您将拥有旧的 Object而不是新的Object。
法律(通常是气馁)是这样的:
private void changeDate(final Date date) { date.setTime(1234567890L); }
我选择Date
是有原因的。 这是一个人们普遍认同应该永远不可变的课程。 上面的方法将更改传递给它的任何Date
对象的内部值。 当非常清楚该方法将改变或配置或修改传入的内容时,这种代码是合法的。
注意:一般来说,一个方法应该做这些事情:
- 返回void并改变其传入的对象(如
Collections.sort()
)或 - 返回一些计算并且根本不改变传入的对象(如
Collections.min()
),或 - 返回传入对象的“视图”但不修改传入对象(如
Collections.checkedList()
或Collections.singleton()
) - 改变一个传入的对象并返回它(
Collections
没有示例,但StringBuilder.append()
就是一个很好的例子)。
改变传入对象并返回单独返回值的方法通常做太多事情。
当然有一些方法可以修改作为参数传入的对象( 请参阅java.io.Reader.read(byte [] buffer)作为示例,但我没有看到用作返回值的替代值的参数,尤其是多个参数。它可能在技术上有效,但它是非标准的。
它通常不被认为是非常好的做法,但在JDK中偶尔会出现这种情况。 查看View.getNextVisualPositionFrom()和相关方法的’biasRet’参数,例如:它实际上是一个用“额外返回值”填充的一维数组。
那么为什么呢? 好吧,只是为了节省你必须为“偶尔的额外返回值”创建一个额外的类定义。 这是凌乱,不优雅,糟糕的设计,非面向对象,等等等等。 我们都不时做到了……
一般来说埃迪说的是什么,但我还要补充一点:
- 改变其中一个传入对象,并返回状态代码。 这通常只应用于显式缓冲区的参数,如Reader.read(char [] cbuf)。
我有一个Result对象,它通过一系列validationvoid方法作为方法参数进行级联。 这些validationvoid方法中的每一个都会改变结果参数对象以添加validation结果。
但这是不可能测试的,因为现在我不能将void方法存根,以便在Result对象中返回validation的存根值。
因此,从测试的角度来看,似乎应该支持返回对象而不是改变方法参数。