返回多个值的Java方法的最佳实践? [复制]

Posted

技术标签:

【中文标题】返回多个值的Java方法的最佳实践? [复制]【英文标题】:Best practice for a Java method returning multiple values? [duplicate] 【发布时间】:2016-11-23 16:44:21 【问题描述】:

我需要一个非静态实例方法来返回多个值。为了一个简单的例子,假设它们是boolean successObject obj - 但在一般情况下,可能会有更多,它们可能彼此无关。可以想出几种不同的方法来做到这一点:

解决方案 1

private boolean aMethod(int aParam, Object obj)  ...set obj parameter & return value... 

解决方案 2

private Pair<Boolean, Object> aMethod(int aParam)  ...set return pair values... 

解决方案 3

private Object obj;
...
private boolean aMethod(int aParam)  ...set obj field & return value... 

解决方案 4

private class MethodReturn  // Nested class - could be a separate class instead
    boolean success;
    Object obj;
    // ... Getters and setters omitted for brevity


private MethodReturn aMethod(int aParam)  ...set return object values... 

还有其他我可能错过的可能性吗?任何人都可以评论每种方法的优缺点(理想情况下,在大多数情况下哪个可能是最好的)?

【问题讨论】:

好问题。在大多数情况下,我个人使用解决方案 2。 这应该在 CodeReview 中移动 解决方案 2 和 4 是最不令人惊讶的,因为至少它们没有副作用,并且更清楚地说明了该方法的作用。如果您决定要返回其他内容,解决方案四将更容易重构。我首先会质疑这种方法的必要性,因为它似乎有不止一个责任,并建议重构以避免这种情况, 我习惯于引用 dot net 但在 Java 中有吗? ***.com/questions/2806545/… @MarioAlexandroSantini 除非 OP 发布了他们真正的实际工作代码,而不是用 cmets 替换代码的方法框架,否则这将与代码审查无关。在将帖子引用到该站点之前,请参阅 A guide to Code Review for Stack Overflow users。 【参考方案1】:

解决方案 5 — 回调

通过回调,您甚至可以返回多个 2 元组

private void aMethod(int aParam, BiConsumer<Boolean, String> callback) 
    …
    callback.accept(success1, msg1);
    …
    callback.accept(success2, msg2);
    …

用法:

aMethod(42, (success, msg) -> 
    if (!success) 
        log.error(msg);
    
);

此外,您可以通过组合返回一个 (n>2)-tuple,而无需自定义类 - 例如对于 4 元组:

<A, B, C, D> void aMethod(int aParam, BiFunction<A, B, BiConsumer<C, D>> callback) 
    callback.apply(a, b).accept(c, d);


aMethod(42, (a, b) -> (c, d) -> log.debug("" + a + b + c + d));

评论您的解决方案:

解决方案 1:修改参数是一种不好的做法,而且 String 是不可变的 解决方案2:常用模式,Pair&lt;T,U&gt;Tuple2&lt;T,U&gt; 解决方案 3:脆弱且不受欢迎,它具有状态完整的所有缺点(并发等) 解决方案 4:类似于 #2,例如class MethodReturn implements Pair&lt;Boolean, String&gt;

注意:MethodReturn 可以是您在运行中(在返回点)实现的接口,例如:

private MethodReturn aMethod(int aParam) 
    …
    return new MethodReturn() 
        @Override
        public Boolean first()  return success; 

        @Override
        public String second()  return msg; 
    ;

【讨论】:

Re:解决方案 2 评论 - 您指的是 this answer 中的自定义 Tuple2 实现吗? @SteveChambers:是的,或者任何其他的,比如javatuples。但是,正如Tuples 文章中所解释的,函数参数 也形成了一个 tuple,因此调用 callback 直接等效于 返回一个元组。此外,回调可以被接收函数多次调用。 这对于一个简单的方法来说是不必要的复杂。它在从线程程序和 Futures 中返回值方面很有用,但它使理解单线程程序中的流控制变得复杂。【参考方案2】:

使用

Object[] aMethod() 

//Create and fill an array of object and return it


您可以根据需要向调用者填写尽可能多的不同值。

【讨论】:

这样移动数据会很麻烦,就像你为了卖一个冰淇淋而移动整辆货车 它的工作,但这个方法运行缓慢【参考方案3】:

如果结果是固定的,你可以在这里使用枚举。

public Enum Status 

     OK("OK"),
     CREATED("Created"),
     ACCEPTED("Accepted"),
     NO_CONTENT("No Content"),
     FORBIDDEN("Forbidden"),
     NOT_FOUND("Not Found");

     private final String reason;

     Status(final String reasonPhrase) 
        this.reason = reasonPhrase;
    


 private Status aMethod(int aParam) 
  ................
  return Status.OK;
 

【讨论】:

或者甚至将其与解决方案#2:enum Status implements Pair&lt;Boolean, String&gt; 结合使用,然后让private Pair&lt;Boolean, String&gt; aMethod() 决定何时返回预定义状态,如return Status.OK; 或新状态,如return pairOf(success, msg);,具体取决于情况。 认为这个答案是指这里的示例/用例,而不是参数可能不是枚举的一般情况。【参考方案4】:

解决方案 1 不起作用,因为 java 中的字符串是不可变的,

您不能总是应用解决方案 3,因为您将结果保存在实例中,只要您需要结果,您可能也无法从该实例运行您的方法。

因此,我们得到了非常相似的解决方案 2 和 4。在这两种情况下,您都会返回包装在自定义类中的结果。如果您认为 Pair 对您来说已经足够了,我会说,使用它,然后使用 2 号解决方案。

【讨论】:

感谢您对解决方案 1 的回答和好地方!我也不喜欢解决方案 3 的想法,不确定我是否完全理解您的评论,但它是否适用,因为它是一种私有方法? (但是我认为这个解决方案也可能存在并发问题,所以几乎没有折扣。)我猜 2 和 4 之间的选择很可能归结为个人喜好,目前倾向于 4... 更新:现在已将问题更改为使用对象而不是字符串来解决解决方案 1 问题。【参考方案5】:

一般情况下,我会根据具体情况选择第 4 个或 Map,但如果您需要返回多个不相关的值,我认为您有一个严重的设计问题(检查 https://en.wikipedia.org/wiki/Single_responsibility_principle)

在特定情况下(在您发表评论后),我肯定会使用所有必填字段的第 4 次建模响应。可能您还可以使用 ResponseSuccessful 和 ResponseFailure 进行子类型化。

【讨论】:

感谢您的回答。为了稍微详细说明实际用例,Spring-MVC 控制器中的私有保存方法返回操作是否成功以及要重定向到的页面的指示。我可能是错的,但不要认为这违反了 SRP。 那么它们不是不相关的,您可以在 Response 对象中对它进行建模,该对象对 HTTP 请求(或其中的一部分)进行建模 @chrylis:并不总是一个好的选择,例如从函数式编程 POV 来看,抛出异常就像 goto 跳转。最好使用Either&lt;E,T&gt;Optional&lt;T&gt; 或回调来处理错误。 @charlie 函数式管道是与标准 Java 代码完全不同的范例。当然,类似 Promise 的回调链在那里更有意义,但这是一个不同的环境。 我同意仅在特殊情况下才应使用异常,而不应出于验证失败等常见原因。显然,现实世界的应用程序(和框架!)并不总是同意这一点!【参考方案6】:

在我的程序中,如果返回的对象在逻辑上与某些事物相关,它们可能需要有自己的类:booleanString 可能是 MessageStatus(编辑:我知道这是你的第四个解决方案)。 给你一个具体的例子:

public class TurnToken 
/**
 * Number of available Quick Actions
 */
private int quickActionTimes;
/**
 * Number of available Main Actions
 */
private int mainActionTimes;
[...Etcetera with setters and getters]

这个类只由两个整数组成,但它在逻辑上表示某些东西可能被认为是一个实体本身

编辑:我不小心取消了第一部分。如果您的对象不是原始的,而是逻辑相关的,您可以创建一个接口来收集它们:

public interface Adjectivable
//If they have a commong method even better

你的方法

public List<Adjectivable> myMultiReturningMethod
    List<Adjectivable> theStuffToReturn = new ArrayList<>();
    Etcetera...

【讨论】:

以上是关于返回多个值的Java方法的最佳实践? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

关于引用参数或显式返回值的 PHP 方法的最佳实践

从函数返回多个值的最佳方法是啥?

从函数返回多个值的最佳方法是啥?

DAO 级别布尔方法的最佳实践

在 Java 中检查空值的最佳方法是啥?

JDK 8 函数式编程最佳实践