【问题标题】:How to return multiple objects from a Java method?如何从 Java 方法返回多个对象?
【发布时间】:2010-10-02 05:45:16
【问题描述】:

我想从一个 Java 方法返回两个对象,并且想知道这样做的好方法是什么?

我能想到的可能方法是:返回一个HashMap(因为这两个对象是相关的)或返回一个ArrayList of Object 对象。

更准确地说,我要返回的两个对象是 (a) List 的对象和 (b) 逗号分隔的相同名称。

我想从一种方法中返回这两个对象,因为我不想遍历对象列表以获取逗号分隔的名称(我可以在此方法的同一循环中执行此操作)。

不知何故,返回 HashMap 看起来不是一种非常优雅的方式。

【问题讨论】:

  • List 和 CSV 是否基本上是相同数据的不同视图?听起来您需要一个对象,其中有一个 List 引用和一个查找表。

标签: java return


【解决方案1】:

如果你想返回两个对象,你通常希望返回一个封装这两个对象的对象。

您可以像这样返回NamedObject 对象列表:

public class NamedObject<T> {
  public final String name;
  public final T object;

  public NamedObject(String name, T object) {
    this.name = name;
    this.object = object;
  }
}

然后您可以轻松返回List&lt;NamedObject&lt;WhateverTypeYouWant&gt;&gt;

另外:为什么要返回以逗号分隔的名称列表而不是 List&lt;String&gt;?或者更好的是,返回一个Map&lt;String,TheObjectType&gt;,其中键是对象的名称和值(除非您的对象已指定顺序,在这种情况下,NavigableMap 可能就是您想要的。

【讨论】:

  • 返回逗号分隔列表的原因是:如果我不在这里创建列表,我将不得不在调用者中通过循环遍历对象来执行此操作(需要 CS 值)。也许,我进行了不必要的预优化。
  • 我一直想知道为什么 Java 没有 Pair 类。
  • Jagmal:是的,如果返回逗号分隔列表的唯一理由是这种优化,那就别管它了。
  • 这只有在您要返回的项目属于同一类或至少有一个密切的共同祖先时才有效。我的意思是,使用 Object 代替 WhatTypeYouWant 不是很整洁。
  • @David:我同意在这里使用 Object 不是很整洁,但是再次返回没有任何共同祖先的对象(当然 Object 除外)也不是很整洁。如果你需要的话,我什至会说这是一种代码味道。
【解决方案2】:

如果您知道要返回两个对象,您也可以使用泛型对:

public class Pair<A,B> {
    public final A a;
    public final B b;

    public Pair(A a, B b) {
        this.a = a;
        this.b = b;
    }
};

编辑上述更完整的实现:

package util;

public class Pair<A,B> {

    public static <P, Q> Pair<P, Q> makePair(P p, Q q) {
        return new Pair<P, Q>(p, q);
    }

    public final A a;
    public final B b;

    public Pair(A a, B b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((a == null) ? 0 : a.hashCode());
        result = prime * result + ((b == null) ? 0 : b.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        Pair other = (Pair) obj;
        if (a == null) {
            if (other.a != null) {
                return false;
            }
        } else if (!a.equals(other.a)) {
            return false;
        }
        if (b == null) {
            if (other.b != null) {
                return false;
            }
        } else if (!b.equals(other.b)) {
            return false;
        }
        return true;
    }

    public boolean isInstance(Class<?> classA, Class<?> classB) {
        return classA.isInstance(a) && classB.isInstance(b);
    }

    @SuppressWarnings("unchecked")
    public static <P, Q> Pair<P, Q> cast(Pair<?, ?> pair, Class<P> pClass, Class<Q> qClass) {

        if (pair.isInstance(pClass, qClass)) {
            return (Pair<P, Q>) pair;
        }

        throw new ClassCastException();

    }

}

注释,主要是关于 Java 和泛型的生锈:

  • ab 都是不可变的。
  • makePair 静态方法可帮助您进行样板类型输入,Java 7 中的菱形运算符将减少烦人。有一些工作可以使这个非常好:泛型,但现在应该没问题。 (参考 PECS)
  • hashcodeequals 由 eclipse 生成。
  • cast 方法中的编译时间转换没问题,但似乎不太正确。
  • 我不确定isInstance 中的通配符是否必要。
  • 我只是为了回应 cmets 而写的,仅用于说明目的。

【讨论】:

  • 我通常在我工作的每个代码库中都有这门课。我还要添加:一个 hashCode/equals 实现,可能还有一个静态 isInstance() 和 cast() 方法。
  • 当然,有很多方法可以让这个类更智能、更方便使用。上面的版本包括一次性声明就足够了。
  • @jamesh:你介意在这里详细写下你的配对吗?我想知道在提供“hashCode/equals 实现,可能还有静态 isInstance() 和 cast() 方法”之后它的样子。谢谢。
  • @QiangLi - 我通常生成 hashcode & equals 方法。实例方法 isInstance 接受两个类,并确保实例的 a 和 b 是这些类的实例。 Cast 需要一个 Pair,?> 并小心地将其转换为 Pair。实现应该相当简单(提示:Class.cast()、Class.isInstance())
  • 这是一个非常好的Pair 实现。我要做的一个小改动:向ClassCastException 添加一条消息。否则,如果由于某种原因失败,调试将成为一场噩梦。 (如果你将原始类型抑制警告转换为Pair&lt;?,?&gt;(这可行,因为你只需要来自abObject 方法)。你介意我调整代码吗?
【解决方案3】:

如果您调用的方法是私有的,或者是从一个位置调用的,请尝试

return new Object[]{value1, value2};

调用者长这样:

Object[] temp=myMethod(parameters);
Type1 value1=(Type1)temp[0];  //For code clarity: temp[0] is not descriptive
Type2 value2=(Type2)temp[1];

David Hanak 的 Pair 示例没有语法优势,并且仅限于两个值。

return new Pair<Type1,Type2>(value1, value2);

调用者看起来像:

Pair<Type1, Type2> temp=myMethod(parameters);
Type1 value1=temp.a;  //For code clarity: temp.a is not descriptive
Type2 value2=temp.b;

【讨论】:

  • 对具有类类型控制的好处
  • 恕我直言,不要这样 - 该声明对预期返回值的说明太少了。 AFAIK,更广泛的首选是创建指定返回多少参数以及这些参数类型的泛型类。 Pair&lt;T1, T2&gt;Tuple&lt;T1, T2, T3&gt;Tuple&lt;T1, T2, T3, T4&gt;等。然后具体使用说明Pair&lt;int, String&gt; temp = ...或其他参数的数量和类型。
【解决方案4】:

您可以使用以下任何一种方式:

private static final int RETURN_COUNT = 2;
private static final int VALUE_A = 0;
private static final int VALUE_B = 1;
private static final String A = "a";
private static final String B = "b";

1) 使用数组

private static String[] methodWithArrayResult() {
    //...
    return new String[]{"valueA", "valueB"};
}

private static void usingArrayResultTest() {
    String[] result = methodWithArrayResult();
    System.out.println();
    System.out.println("A = " + result[VALUE_A]);
    System.out.println("B = " + result[VALUE_B]);
}

2) 使用 ArrayList

private static List<String> methodWithListResult() {
    //...
    return Arrays.asList("valueA", "valueB");
}

private static void usingListResultTest() {
    List<String> result = methodWithListResult();
    System.out.println();
    System.out.println("A = " + result.get(VALUE_A));
    System.out.println("B = " + result.get(VALUE_B));
}

3) 使用 HashMap

private static Map<String, String> methodWithMapResult() {
    Map<String, String> result = new HashMap<>(RETURN_COUNT);
    result.put(A, "valueA");
    result.put(B, "valueB");
    //...
    return result;
}

private static void usingMapResultTest() {
    Map<String, String> result = methodWithMapResult();
    System.out.println();
    System.out.println("A = " + result.get(A));
    System.out.println("B = " + result.get(B));
}

4) 使用您的自定义容器类

private static class MyContainer<M,N> {
    private final M first;
    private final N second;

    public MyContainer(M first, N second) {
        this.first = first;
        this.second = second;
    }

    public M getFirst() {
        return first;
    }

    public N getSecond() {
        return second;
    }

    // + hashcode, equals, toString if need
}

private static MyContainer<String, String> methodWithContainerResult() {
    //...
    return new MyContainer("valueA", "valueB");
}

private static void usingContainerResultTest() {
    MyContainer<String, String> result = methodWithContainerResult();
    System.out.println();
    System.out.println("A = " + result.getFirst());
    System.out.println("B = " + result.getSecond());
}

5) 使用 AbstractMap.simpleEntry

private static AbstractMap.SimpleEntry<String, String> methodWithAbstractMapSimpleEntryResult() {
    //...
    return new AbstractMap.SimpleEntry<>("valueA", "valueB");
}

private static void usingAbstractMapSimpleResultTest() {
    AbstractMap.SimpleEntry<String, String> result = methodWithAbstractMapSimpleEntryResult();
    System.out.println();
    System.out.println("A = " + result.getKey());
    System.out.println("B = " + result.getValue());
}

6) 使用 PairApache Commons

private static Pair<String, String> methodWithPairResult() {
    //...
    return new ImmutablePair<>("valueA", "valueB");
}

private static void usingPairResultTest() {
    Pair<String, String> result = methodWithPairResult();
    System.out.println();
    System.out.println("A = " + result.getKey());
    System.out.println("B = " + result.getValue());
}

【讨论】:

    【解决方案5】:

    当我用 Java 编码时,我几乎总是最终定义 n-Tuple 类。例如:

    public class Tuple2<T1,T2> {
      private T1 f1;
      private T2 f2;
      public Tuple2(T1 f1, T2 f2) {
        this.f1 = f1; this.f2 = f2;
      }
      public T1 getF1() {return f1;}
      public T2 getF2() {return f2;}
    }
    

    我知道这有点难看,但它确实有效,您只需定义一次元组类型。元组是 Java 真正缺乏的东西。

    编辑:David Hanak 的示例更优雅,因为它避免定义 getter 并仍然保持对象不可变。

    【讨论】:

      【解决方案6】:

      在 Java 5 之前,我有点同意 Map 解决方案并不理想。它不会为您提供编译时类型检查,因此可能会在运行时导致问题。但是,在 Java 5 中,我们有了泛型类型。

      所以你的方法可能是这样的:

      public Map<String, MyType> doStuff();
      

      MyType 当然是你要返回的对象的类型。

      基本上我认为在这种情况下返回 Map 是正确的解决方案,因为这正是您想要返回的 - 字符串到对象的映射。

      【讨论】:

      • 如果任何名称发生冲突,这将不起作用。列表可以包含重复项,但 Map 不能(包含重复键)。
      • 当然。我是根据这个问题做出假设的——也许是不恰当的:)
      • 虽然你的假设在这种情况下成立,但我正在进入过早优化的领域(我不应该这样做)。
      【解决方案7】:

      Apache Commons 为此提供了元组和三元组:

      • ImmutablePair&lt;L,R&gt; 由两个 Object 组成的不可变对 元素。
      • ImmutableTriple&lt;L,M,R&gt; 一个不可变的三元组,由 三个 Object 元素。
      • MutablePair&lt;L,R&gt; 一个可变对,由 两个 Object 元素。
      • MutableTriple&lt;L,M,R&gt; 可变三元组 由三个 Object 元素组成。
      • Pair&lt;L,R&gt; 一对由 两个元素。
      • Triple&lt;L,M,R&gt; 由三个元素组成的三元组。

      来源:https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html

      【讨论】:

      • 我们有没有类似的类可以返回五个以上的类
      【解决方案8】:

      另外,在我想从一个方法返回许多东西的情况下,我有时会使用回调机制而不是容器。这在我无法提前指定将生成多少对象的情况下非常有效。

      对于您的特定问题,它看起来像这样:

      public class ResultsConsumer implements ResultsGenerator.ResultsCallback
      {
          public void handleResult( String name, Object value )
          {
              ... 
          }
      }
      
      public class ResultsGenerator
      {
          public interface ResultsCallback
          {
              void handleResult( String aName, Object aValue );
          }
      
          public void generateResults( ResultsGenerator.ResultsCallback aCallback )
          {
              Object value = null;
              String name = null;
      
              ...
      
              aCallback.handleResult( name, value );
          }
      }
      

      【讨论】:

      • 很抱歉评论您的一个非常古老的答案,但是关于垃圾收集的回调如何处理?我当然对java中的内存管理没有很好的理解,如果你有对象A调用对象B.getResult()B.getResult()调用A.finishResult()作为callback,对象B得到垃圾收集还是它会一直停留到 A 完成?可能是一个愚蠢的问题,但我有一个根本的困惑!
      【解决方案9】:

      虽然在您的情况下,the comment 可能是一个不错的选择,但在 Android 中,您可以使用 Pair 。简单的

      return new Pair<>(yourList, yourCommaSeparatedValues);
      

      【讨论】:

        【解决方案10】:

        使用以下 Entry 对象 示例:

        public Entry<A,B> methodname(arg)
        {
        .......
        
        return new AbstractMap.simpleEntry<A,B>(instanceOfA,instanceOfB);
        }
        

        【讨论】:

          【解决方案11】:

          一般来说,关于多个返回值的问题,我通常使用一个小的帮助类来包装单个返回值并作为参数传递给方法:

          public class ReturnParameter<T> {
              private T value;
          
              public ReturnParameter() { this.value = null; }
              public ReturnParameter(T initialValue) { this.value = initialValue; }
          
              public void set(T value) { this.value = value; }
              public T get() { return this.value; }
          }
          

          (对于原始数据类型,我使用较小的变化来直接存储值)

          一个想要返回多个值的方法将被声明如下:

          public void methodThatReturnsTwoValues(ReturnParameter<ClassA> nameForFirstValueToReturn, ReturnParameter<ClassB> nameForSecondValueToReturn) {
              //...
              nameForFirstValueToReturn.set("...");
              nameForSecondValueToReturn.set("...");
              //...
          }
          

          也许主要的缺点是调用者必须提前准备返回对象以防他想使用它们(并且该方法应该检查空指针)

          ReturnParameter<ClassA> nameForFirstValue = new ReturnParameter<ClassA>();
          ReturnParameter<ClassB> nameForSecondValue = new ReturnParameter<ClassB>();
          methodThatReturnsTwoValues(nameForFirstValue, nameForSecondValue);
          

          优势(与其他提议的解决方案相比):

          • 您不必为各个方法及其返回类型创建特殊的类声明
          • 参数有一个名称,因此在查看方法签名时更容易区分
          • 每个参数的类型安全

          【讨论】:

          • 感谢为每个返回值提供名称和类型安全性的解决方案,而无需为每组返回值类型进行类声明。
          【解决方案12】:

          所有可能的解决方案都将是一个杂项(如容器对象、您的 HashMap 想法、通过数组实现的“多个返回值”)。我建议从返回的列表中重新生成逗号分隔的列表。代码最终会变得更干净。

          【讨论】:

          • 我同意你的观点,但如果我这样做,我最终会循环两次(我实际上是在现有方法中一个接一个地创建列表的元素)。
          • @Jagmal:您可能会循环两次,但大多数时候都没有关系(请参阅 gizmos 答案)。
          • 是的,除非你真的需要,否则不要尝试优化你的代码。 Gizmo 对此非常正确。
          【解决方案13】:

          保持简单并为多个结果情况创建一个类。此示例接受来自数据库助手 getInfo 的 ArrayList 和消息文本。

          您在哪里调用返回多个您编码的值的例程:

          multResult res = mydb.getInfo(); 
          

          在你编码的例程 getInfo 中:

          ArrayList<String> list= new ArrayList<String>();
          add values to the list...
          return new multResult("the message", list);
          

          并定义一个类 multResult :

          public class multResult {
              public String message; // or create a getter if you don't like public
              public ArrayList<String> list;
              multResult(String m, ArrayList<String> l){
                  message = m;
                  list= l;
          }
          

          }

          【讨论】:

            【解决方案14】:

            在我看来,这里确实有三个选择,解决方案取决于上下文。您可以选择在生成列表的方法中实现名称的构造。这是您选择的选择,但我认为这不是最好的选择。您正在生产者方法中创建与不需要存在的消费方法的耦合。其他来电者可能不需要额外信息,而您需要为这些来电者计算额外信息。

            或者,您可以让调用方法计算名称。如果只有一个呼叫者需要此信息,您可以停在那里。您没有额外的依赖关系,虽然涉及一些额外的计算,但您避免了使您的构造方法过于具体。这是一个很好的权衡。

            最后,您可以让列表本身负责创建名称。如果计算需要由多个调用者完成,这是我会走的路线。我认为这将负责创建与对象本身最密切相关的类的名称。

            在后一种情况下,我的解决方案是创建一个专门的 List 类,该类返回它包含的对象名称的逗号分隔字符串。使类足够智能,以便在添加和删除对象时动态构造名称字符串。然后返回这个列表的一个实例,并根据需要调用名称生成方法。尽管将名称的计算简单地延迟到第一次调用该方法并随后存储它(延迟加载)可能几乎同样有效(且更简单)。如果您添加/删除一个对象,您只需删除计算的值并在下次调用时重新计算它。

            【讨论】:

              【解决方案15】:

              可以在动态语言(Python)中做一些类似元组的事情

              public class Tuple {
              private Object[] multiReturns;
              
              private Tuple(Object... multiReturns) {
                  this.multiReturns = multiReturns;
              }
              
              public static Tuple _t(Object... multiReturns){
                  return new Tuple(multiReturns);
              }
              
              public <T> T at(int index, Class<T> someClass) {
                  return someClass.cast(multiReturns[index]);
              }
              }
              

              并像这样使用

              public Tuple returnMultiValues(){
                 return Tuple._t(new ArrayList(),new HashMap())
              }
              
              
              Tuple t = returnMultiValues();
              ArrayList list = t.at(0,ArrayList.class);
              

              【讨论】:

                【解决方案16】:

                我采用了与其他答案中描述的方法类似的方法,并根据我的要求进行了一些调整,基本上我创建了以下类(以防万一,一切都是 Java):

                public class Pair<L, R> {
                    final L left;
                    final R right;
                
                    public Pair(L left, R right) {
                        this.left = left;
                        this.right = right;
                    }
                
                    public <T> T get(Class<T> param) {
                        return (T) (param == this.left.getClass() ? this.left : this.right);
                    }
                
                    public static <L, R> Pair<L, R> of(L left, R right) {
                        return new Pair<L, R>(left, right);
                    }
                }
                

                然后,我的要求很简单,在到达数据库的存储库类中,对于获取方法而不是从数据库中检索数据,我需要检查它是失败还是成功,然后,如果成功,我需要玩返回列表,如果失败则停止执行并通知错误。

                所以,例如,我的方法是这样的:

                public Pair<ResultMessage, List<Customer>> getCustomers() {
                    List<Customer> list = new ArrayList<Customer>();
                    try {
                    /*
                    * Do some work to get the list of Customers from the DB
                    * */
                    } catch (SQLException e) {
                        return Pair.of(
                                       new ResultMessage(e.getErrorCode(), e.getMessage()), // Left 
                                       null);  // Right
                    }
                    return Pair.of(
                                   new ResultMessage(0, "SUCCESS"), // Left 
                                   list); // Right
                }
                

                其中 ResultMessage 只是一个具有两个字段(代码/消息)的类,而 Customer 是具有来自数据库的一堆字段的任何类。

                然后,为了检查结果,我只是这样做:

                void doSomething(){
                    Pair<ResultMessage, List<Customer>> customerResult = _repository.getCustomers();
                    if (customerResult.get(ResultMessage.class).getCode() == 0) {
                        List<Customer> listOfCustomers = customerResult.get(List.class);
                        System.out.println("do SOMETHING with the list ;) ");
                    }else {
                        System.out.println("Raised Error... do nothing!");
                    }
                }
                

                【讨论】:

                  【解决方案17】:

                  在 C++ (STL) 中,有一个用于捆绑两个对象的 pair 类。在 Java 泛型中,pair 类不可用,尽管有一些 demand 用于它。不过,您可以轻松地自己实现它。

                  但我同意其他一些答案,即如果您需要从一个方法返回两个或多个对象,最好将它们封装在一个类中。

                  【讨论】:

                    【解决方案18】:

                    为什么不创建一个包含您的结果的WhateverFunctionResult 对象,解析这些结果所需的逻辑,然后迭代等等。在我看来,要么:

                    1. 这些结果对象紧密联系在一起/相关并且属于一起,或者:
                    2. 它们是不相关的,在这种情况下,您的函数没有很好地定义它试图做什么(即做两件不同的事情)

                    我看到这类问题一次又一次地出现。不要害怕创建自己的容器/结果类,其中包含数据和相关的功能来处理它。如果你只是简单地以HashMap 或类似的方式传递这些东西,那么你的客户每次想要使用结果时都必须拆开这张地图并摸索内容。

                    【讨论】:

                    • 因为它是一个 PITA,当您需要返回多个值时必须定义一个类,仅仅是因为该语言缺乏这个普遍有用的功能;)但是说真的,您的建议通常值得做。
                    【解决方案19】:
                    public class MultipleReturnValues {
                    
                        public MultipleReturnValues() {
                        }
                    
                        public static void functionWithSeveralReturnValues(final String[] returnValues) {
                            returnValues[0] = "return value 1";
                            returnValues[1] = "return value 2";
                        }
                    
                        public static void main(String[] args) {
                            String[] returnValues = new String[2];
                            functionWithSeveralReturnValues(returnValues);
                            System.out.println("returnValues[0] = " + returnValues[0]);
                            System.out.println("returnValues[1] = " + returnValues[1]);
                        }
                    
                    }
                    

                    【讨论】:

                      【解决方案20】:

                      这并不能完全回答问题,但由于这里给出的每个解决方案都有一些缺点,我建议尝试稍微重构你的代码,这样你只需要返回一个值。

                      案例一。

                      你需要在你的方法内外都有一些东西。为什么不在外面计算出来传给方法呢?

                      代替:

                      [thingA, thingB] = createThings(...);  // just a conceptual syntax of method returning two values, not valid in Java
                      

                      试试:

                      thingA = createThingA(...);
                      thingB = createThingB(thingA, ...);
                      

                      这应该可以满足您的大部分需求,因为在大多数情况下,一个值是在另一个值之前创建的,您可以通过两种方法拆分创建它们。缺点是方法createThingsBcreateThings 相比有一个额外的参数,并且可能您将完全相同的参数列表两次传递给不同的方法。


                      案例二。

                      有史以来最明显的解决方案和案例一的简化版本。这并不总是可能的,但也许这两个值都可以相互独立地创建?

                      代替:

                      [thingA, thingB] = createThings(...);  // see above
                      

                      试试:

                      thingA = createThingA(...);
                      thingB = createThingB(...);
                      

                      为了更有用,这两种方法可以共享一些共同的逻辑:

                      public ThingA createThingA(...) {
                          doCommonThings(); // common logic
                          // create thing A
                      }
                      public ThingB createThingB(...) {
                          doCommonThings(); // common logic
                          // create thing B
                      }
                      

                      【讨论】:

                        【解决方案21】:

                        将一个列表传递给您的方法并填充它,然后返回带有名称的字符串,如下所示:

                        public String buildList(List<?> list) {
                            list.add(1);
                            list.add(2);
                            list.add(3);
                            return "something,something,something,dark side";
                        }
                        

                        然后这样称呼它:

                        List<?> values = new ArrayList<?>();
                        String names = buildList(values);
                        

                        【讨论】:

                          【解决方案22】:

                          您可以使用HashMap&lt;String, Object&gt;,如下所示

                          public HashMap<String, Object> yourMethod()
                          {
                          
                             .... different logic here 
                          
                            HashMap<String, Object> returnHashMap = new HashMap<String, Object>();
                            returnHashMap.put("objectA", objectAValue);
                            returnHashMap.put("myString", myStringValue);
                            returnHashMap.put("myBoolean", myBooleanValue);
                          
                            return returnHashMap;
                          }
                          

                          然后当在不同的范围内调用方法时,你可以将每个对象转换回它的初始类型:

                          // call the method
                          HashMap<String, Object> resultMap = yourMethod();
                                          
                          // fetch the results and cast them
                          ObjectA objectA = (ObjectA) resultMap.get("objectA");
                          String myString = (String) resultMap.get("myString");
                          Boolean myBoolean = (Boolean) resultMap.get("myBoolean");
                          

                          【讨论】:

                            【解决方案23】:

                            我注意到目前还没有返回多个值的非自定义类、n 长度、非强制类型转换、类型安全的答案。

                            这是我的出发点:

                            import java.util.Objects;
                            
                            public final class NTuple<V, T extends  NTuple<?, ?>> {
                                private final V value;
                                private final T next;
                            
                                private NTuple(V value, T next) {
                                    this.value = value;
                                    this.next = next;
                                }
                            
                                public static <V> NTuple<V, ?> of(V value) {
                                    return new NTuple<>(value, null);
                                }
                            
                                public static <V,  T extends  NTuple<?, ?>> NTuple<V, T> of(V value, T next) {
                                    return new NTuple<>(value, next);
                                }
                            
                                public V value() {
                                    return value;
                                }
                            
                                public T next() {
                                    return next;
                                }
                            
                                public static <V> V unpack0(NTuple<V, ?> tuple) {
                                    return Objects.requireNonNull(tuple, "0").value();
                                }
                            
                                public static <V, T extends NTuple<V, ?>> V unpack1(NTuple<?, T> tuple) {
                                    NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
                                    NTuple<V, ?> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
                                    return tuple1.value();
                                }
                            
                                public static <V, T extends NTuple<?, NTuple<V, ?>>> V unpack2(NTuple<?, T> tuple) {
                                    NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
                                    NTuple<?, NTuple<V, ?>> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
                                    NTuple<V, ?> tuple2 = Objects.requireNonNull(tuple1.next(), "2");
                                    return tuple2.value();
                                }
                            }
                            

                            使用示例:

                            public static void main(String[] args) {
                                // pre-java 10 without lombok - use lombok's var or java 10's var if you can
                                NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> multiple = wordCount("hello world");
                                String original = NTuple.unpack0(multiple);
                                Integer wordCount = NTuple.unpack1(multiple);
                                Integer characterCount  = NTuple.unpack2(multiple);
                            
                                System.out.println(original + ": " + wordCount + " words " + characterCount + " chars");
                            }
                            
                            private static NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> wordCount(String s) {
                                int nWords = s.split(" ").length;
                                int nChars = s.length();
                                return NTuple.of(s, NTuple.of(nWords, NTuple.of(nChars)));
                            }
                            

                            优点:

                            • 无自定义容器类 - 无需仅为返回类型编写类
                            • n-length - 可以处理任意数量的返回值
                            • no-cast - 无需从 Object 进行投射
                            • 类型安全 - 通过 Java 的泛型检查类型

                            缺点:

                            • 对于大量返回值效率低下
                              • 根据我对 python 的多个返回值的经验,这在实践中不应该发生
                            • 重类型声明
                              • 可以通过 lombok/Java 10 var 缓解

                            【讨论】:

                              【解决方案24】:

                              在 C 中,您可以通过将指向占位符的指针作为参数传递给结果:

                              void getShoeAndWaistSizes(int *shoeSize, int *waistSize) {
                                  *shoeSize = 36;
                                  *waistSize = 45;
                              }
                              ...
                              int shoeSize, waistSize;
                              getShoeAndWaistSize(&shoeSize, &waistSize);
                              int i = shoeSize + waistSize;
                              

                              让我们在 Java 中尝试类似的东西。

                              void getShoeAndWaistSizes(List<Integer> shoeSize, List<Integer> waistSize) {
                                  shoeSize.add(36);
                                  waistSize.add(45);
                              }
                              ...
                              List<Integer> shoeSize = new List<>();
                              List<Integer> waistSize = new List<>();
                              getShoeAndWaistSizes(shoeSize, waistSize);
                              int i = shoeSize.get(0) + waistSize.get(0);
                              

                              【讨论】:

                              • 但是在 OO 语言中,通常认为最好做一些人在此答案之前四年已经建议的事情:将两个相关值组合到一个对象中(对、元组或自定义类定义) ,然后有这些对象的列表。这样做可以避免传递多个列表。如果需要将这样的一对(每个列表的一个元素)传递给其他方法以进行进一步处理,这一点变得尤为重要。
                              • @ToolmakerSteve 澄清一下:这些列表每个都只有一个元素,并且只是一种实现类似于传递指针的方法。它们的目的不是为了收集多个结果,甚至在方法调用后的几行之外使用。
                              【解决方案25】:

                              将哈希传递给方法并填充它......

                              public void buildResponse(String data, Map response);

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2012-01-24
                                • 2021-06-10
                                • 1970-01-01
                                • 2020-11-01
                                • 1970-01-01
                                • 2020-03-06
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多