Java方法参数是否应该用于返回多个值?

时间:2021-03-02 05:29:00

Since arguments sent to a method in Java point to the original data structures in the caller method, did its designers intend for them to used for returning multiple values, as is the norm in other languages like C ?

由于发送到Java中的方法的参数指向调用方法中的原始数据结构,因此设计者是否打算将它们用于返回多个值,这是其他语言(例如C)的常规吗?

Or is this a hazardous misuse of Java's general property that variables are pointers ?

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

9 个解决方案

#1


A long time ago I had a conversation with Ken Arnold (one time member of the Java team), this would have been at the first Java One conference probably, so 1996. He said that they were thinking of adding multiple return values so you could write something like:

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

x, y = foo();

The recommended way of doing it back then, and now, is to make a class that has multiple data members and return that instead.

当时推荐的方法是创建一个具有多个数据成员的类,然后返回该类。

Based on that, and other comments made by people who worked on Java, I would say the intent is/was that you return an instance of a class rather than modify the arguments that were passed in.

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

This is common practice (as is the desire by C programmers to modify the arguments... eventually they see the Java way of doing it usually. Just think of it as returning a struct. :-)

这是常见的做法(因为C程序员希望修改参数......最终他们通常会看到Java的方式。只需将其视为返回结构。:-)

(Edit based on the following comment)

(根据以下评论编辑)

I am reading a file and generating two arrays, of type String and int from it, picking one element for both from each line. I want to return both of them to any function which calls it which a file to split this way.

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

I think, if I am understanding you correctly, tht I would probably do soemthing like this:

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

// 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
}

and then have your method like this:

然后让你的方法像这样:

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

and then call it like this:

然后像这样调用它:

{
    final List<Line> lines;

    lines = new ArrayList<Line>();
    foo(file, lines);
}

#2


In my opinion, if we're talking about a public method, you should create a separate class representing a return value. When you have a separate class:

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

  • it serves as an abstraction (i.e. a Point class instead of array of two longs)
  • 它作为一个抽象(即一个Point类而不是两个longs的数组)

  • each field has a name
  • 每个字段都有一个名称

  • can be made immutable
  • 可以变得一成不变

  • makes evolution of API much easier (i.e. what about returning 3 instead of 2 values, changing type of some field etc.)
  • 使API的演化更容易(即返回3而不是2个值,改变某些字段的类型等)

I would always opt for returning a new instance, instead of actually modifying a value passed in. It seems much clearer to me and favors immutability.

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

On the other hand, if it is an internal method, I guess any of the following might be used:

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

  • an array (new Object[] { "str", longValue })
  • 一个数组(new Object [] {“str”,longValue})

  • a list (Arrays.asList(...) returns immutable list)
  • 列表(Arrays.asList(...)返回不可变列表)

  • pair/tuple class, such as this
  • pair / tuple类,比如这个

  • static inner class, with public fields
  • 静态内部类,带有公共字段

Still, I would prefer the last option, equipped with a suitable constructor. That is especially true if you find yourself returning the same tuple from more than one place.

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

#3


I do wish there was a Pair<E,F> class in JDK, mostly for this reason. There is Map<K,V>.Entry, but creating an instance was always a big pain.

我希望JDK中有一个Pair 类,主要是出于这个原因。有Map .Entry,但创建一个实例总是很痛苦。 ,v> ,f>

Now I use com.google.common.collect.Maps.immutableEntry when I need a Pair

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

#4


See this RFE launched back in 1999:

见1999年推出的RFE:

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

I don't think the intention was to ever allow it in the Java language, if you need to return multiple values you need to encapsulate them in an object.

如果你需要返回多个值来将它们封装在一个对象中,我不认为它的目的是在Java语言中允许它。

Using languages like Scala however you can return tuples, see:

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

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

You can also use Generics in Java to return a pair of objects, but that's about it AFAIK.

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

EDIT: Tuples

Just to add some more on this. I've previously implemented a Pair in projects because of the lack within the JDK. Link to my implementation is here:

只是为此添加更多内容。我之前在项目中实现了一对,因为JDK内部缺乏。链接到我的实现在这里:

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

Note, there isn't a hashcode or equals on this, which should probably be added.

注意,没有哈希码或等于此,应该添加。

I also came across this whilst doing some research into this questions which provides tuple functionality:

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

http://javatuple.com/

It allows you to create Pair including other types of tuples.

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

#5


You cannot truly return multiple values, but you can pass objects into a method and have the method mutate those values. That is perfectly legal. Note that you cannot pass an object in and have the object itself become a different object. That is:

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

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

will result in temporarily and locally changing the value of a, but this will not change the value of the caller, for example, from:

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

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

After myFunc returns, you will have the old Object and not the new one.

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

Legal (and often discouraged) is something like this:

法律(通常是气馁)是这样的:

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

I picked Date for a reason. This is a class that people widely agree should never have been mutable. The the method above will change the internal value of any Date object that you pass to it. This kind of code is legal when it is very clear that the method will mutate or configure or modify what is being passed in.

我选择日期是有原因的。这是一个人们普遍认同应该永远不可变的课程。上面的方法将更改传递给它的任何Date对象的内部值。当非常清楚该方法将改变或配置或修改传入的内容时,这种代码是合法的。

NOTE: Generally, it's said that a method should do one these things:

注意:一般来说,一个方法应该做这些事情:

  • Return void and mutate its incoming objects (like Collections.sort()), or
  • 返回void并改变其传入的对象(如Collections.sort())或

  • Return some computation and don't mutate incoming objects at all (like Collections.min()), or
  • 返回一些计算并且根本不改变传入的对象(如Collections.min()),或

  • Return a "view" of the incoming object but do not modify the incoming object (like Collections.checkedList() or Collections.singleton())
  • 返回传入对象的“视图”但不修改传入对象(如Collections.checkedList()或Collections.singleton())

  • Mutate one incoming object and return it (Collections doesn't have an example, but StringBuilder.append() is a good example).
  • 改变一个传入的对象并返回它(Collections没有示例,但StringBuilder.append()就是一个很好的例子)。

Methods that mutate incoming objects and return a separate return value are often doing too many things.

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

#6


There are certainly methods that modify an object passed in as a parameter (see java.io.Reader.read(byte[] buffer) as an example, but I have not seen parameters used as an alternative for a return value, especially with multiple parameters. It may technically work, but it is nonstandard.

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

#7


It's not generally considered terribly good practice, but there are very occasional cases in the JDK where this is done. Look at the 'biasRet' parameter of View.getNextVisualPositionFrom() and related methods, for example: it's actually a one-dimensional array that gets filled with an "extra return value".

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

So why do this? Well, just to save you having to create an extra class definition for the "occasional extra return value". It's messy, inelegant, bad design, non-object-oriented, blah blah. And we've all done it from time to time...

那么为什么呢?好吧,只是为了节省你必须为“偶尔的额外返回值”创建一个额外的类定义。这是凌乱,不优雅,糟糕的设计,非面向对象,等等等等。我们都不时做到了......

#8


Generally what Eddie said, but I'd add one more:

一般来说埃迪说的是什么,但我还要补充一点:

  • Mutate one of the incoming objects, and return a status code. This should generally only be used for arguments that are explicitly buffers, like Reader.read(char[] cbuf).
  • 改变其中一个传入对象,并返回状态代码。这通常只应用于显式缓冲区的参数,如Reader.read(char [] cbuf)。

#9


I had a Result object that cascades through a series of validating void methods as a method parameter. Each of these validating void methods would mutate the result parameter object to add the result of the validation.

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

But this is impossible to test because now I cannot stub the void method to return a stub value for the validation in the Result object.

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

So, from a testing perspective it appears that one should favor returning a object instead of mutating a method parameter.

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

#1


A long time ago I had a conversation with Ken Arnold (one time member of the Java team), this would have been at the first Java One conference probably, so 1996. He said that they were thinking of adding multiple return values so you could write something like:

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

x, y = foo();

The recommended way of doing it back then, and now, is to make a class that has multiple data members and return that instead.

当时推荐的方法是创建一个具有多个数据成员的类,然后返回该类。

Based on that, and other comments made by people who worked on Java, I would say the intent is/was that you return an instance of a class rather than modify the arguments that were passed in.

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

This is common practice (as is the desire by C programmers to modify the arguments... eventually they see the Java way of doing it usually. Just think of it as returning a struct. :-)

这是常见的做法(因为C程序员希望修改参数......最终他们通常会看到Java的方式。只需将其视为返回结构。:-)

(Edit based on the following comment)

(根据以下评论编辑)

I am reading a file and generating two arrays, of type String and int from it, picking one element for both from each line. I want to return both of them to any function which calls it which a file to split this way.

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

I think, if I am understanding you correctly, tht I would probably do soemthing like this:

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

// 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
}

and then have your method like this:

然后让你的方法像这样:

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

and then call it like this:

然后像这样调用它:

{
    final List<Line> lines;

    lines = new ArrayList<Line>();
    foo(file, lines);
}

#2


In my opinion, if we're talking about a public method, you should create a separate class representing a return value. When you have a separate class:

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

  • it serves as an abstraction (i.e. a Point class instead of array of two longs)
  • 它作为一个抽象(即一个Point类而不是两个longs的数组)

  • each field has a name
  • 每个字段都有一个名称

  • can be made immutable
  • 可以变得一成不变

  • makes evolution of API much easier (i.e. what about returning 3 instead of 2 values, changing type of some field etc.)
  • 使API的演化更容易(即返回3而不是2个值,改变某些字段的类型等)

I would always opt for returning a new instance, instead of actually modifying a value passed in. It seems much clearer to me and favors immutability.

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

On the other hand, if it is an internal method, I guess any of the following might be used:

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

  • an array (new Object[] { "str", longValue })
  • 一个数组(new Object [] {“str”,longValue})

  • a list (Arrays.asList(...) returns immutable list)
  • 列表(Arrays.asList(...)返回不可变列表)

  • pair/tuple class, such as this
  • pair / tuple类,比如这个

  • static inner class, with public fields
  • 静态内部类,带有公共字段

Still, I would prefer the last option, equipped with a suitable constructor. That is especially true if you find yourself returning the same tuple from more than one place.

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

#3


I do wish there was a Pair<E,F> class in JDK, mostly for this reason. There is Map<K,V>.Entry, but creating an instance was always a big pain.

我希望JDK中有一个Pair 类,主要是出于这个原因。有Map .Entry,但创建一个实例总是很痛苦。 ,v> ,f>

Now I use com.google.common.collect.Maps.immutableEntry when I need a Pair

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

#4


See this RFE launched back in 1999:

见1999年推出的RFE:

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

I don't think the intention was to ever allow it in the Java language, if you need to return multiple values you need to encapsulate them in an object.

如果你需要返回多个值来将它们封装在一个对象中,我不认为它的目的是在Java语言中允许它。

Using languages like Scala however you can return tuples, see:

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

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

You can also use Generics in Java to return a pair of objects, but that's about it AFAIK.

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

EDIT: Tuples

Just to add some more on this. I've previously implemented a Pair in projects because of the lack within the JDK. Link to my implementation is here:

只是为此添加更多内容。我之前在项目中实现了一对,因为JDK内部缺乏。链接到我的实现在这里:

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

Note, there isn't a hashcode or equals on this, which should probably be added.

注意,没有哈希码或等于此,应该添加。

I also came across this whilst doing some research into this questions which provides tuple functionality:

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

http://javatuple.com/

It allows you to create Pair including other types of tuples.

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

#5


You cannot truly return multiple values, but you can pass objects into a method and have the method mutate those values. That is perfectly legal. Note that you cannot pass an object in and have the object itself become a different object. That is:

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

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

will result in temporarily and locally changing the value of a, but this will not change the value of the caller, for example, from:

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

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

After myFunc returns, you will have the old Object and not the new one.

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

Legal (and often discouraged) is something like this:

法律(通常是气馁)是这样的:

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

I picked Date for a reason. This is a class that people widely agree should never have been mutable. The the method above will change the internal value of any Date object that you pass to it. This kind of code is legal when it is very clear that the method will mutate or configure or modify what is being passed in.

我选择日期是有原因的。这是一个人们普遍认同应该永远不可变的课程。上面的方法将更改传递给它的任何Date对象的内部值。当非常清楚该方法将改变或配置或修改传入的内容时,这种代码是合法的。

NOTE: Generally, it's said that a method should do one these things:

注意:一般来说,一个方法应该做这些事情:

  • Return void and mutate its incoming objects (like Collections.sort()), or
  • 返回void并改变其传入的对象(如Collections.sort())或

  • Return some computation and don't mutate incoming objects at all (like Collections.min()), or
  • 返回一些计算并且根本不改变传入的对象(如Collections.min()),或

  • Return a "view" of the incoming object but do not modify the incoming object (like Collections.checkedList() or Collections.singleton())
  • 返回传入对象的“视图”但不修改传入对象(如Collections.checkedList()或Collections.singleton())

  • Mutate one incoming object and return it (Collections doesn't have an example, but StringBuilder.append() is a good example).
  • 改变一个传入的对象并返回它(Collections没有示例,但StringBuilder.append()就是一个很好的例子)。

Methods that mutate incoming objects and return a separate return value are often doing too many things.

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

#6


There are certainly methods that modify an object passed in as a parameter (see java.io.Reader.read(byte[] buffer) as an example, but I have not seen parameters used as an alternative for a return value, especially with multiple parameters. It may technically work, but it is nonstandard.

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

#7


It's not generally considered terribly good practice, but there are very occasional cases in the JDK where this is done. Look at the 'biasRet' parameter of View.getNextVisualPositionFrom() and related methods, for example: it's actually a one-dimensional array that gets filled with an "extra return value".

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

So why do this? Well, just to save you having to create an extra class definition for the "occasional extra return value". It's messy, inelegant, bad design, non-object-oriented, blah blah. And we've all done it from time to time...

那么为什么呢?好吧,只是为了节省你必须为“偶尔的额外返回值”创建一个额外的类定义。这是凌乱,不优雅,糟糕的设计,非面向对象,等等等等。我们都不时做到了......

#8


Generally what Eddie said, but I'd add one more:

一般来说埃迪说的是什么,但我还要补充一点:

  • Mutate one of the incoming objects, and return a status code. This should generally only be used for arguments that are explicitly buffers, like Reader.read(char[] cbuf).
  • 改变其中一个传入对象,并返回状态代码。这通常只应用于显式缓冲区的参数,如Reader.read(char [] cbuf)。

#9


I had a Result object that cascades through a series of validating void methods as a method parameter. Each of these validating void methods would mutate the result parameter object to add the result of the validation.

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

But this is impossible to test because now I cannot stub the void method to return a stub value for the validation in the Result object.

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

So, from a testing perspective it appears that one should favor returning a object instead of mutating a method parameter.

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