如何从Java方法返回多个对象?

我想从一个Java方法返回两个对象,并想知道怎样才能做到这一点?

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

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

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

不知何故,返回一个HashMap并不是一个非常优雅的方式。

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

你可以像这样返回一个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<NamedObject<WhateverTypeYouWant>>

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

如果你知道你要返回两个对象,你也可以使用一个通用对:

 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和generics生锈:

  • ab都是不变的。
  • makePair静态方法可以帮助您进行锅炉板打字,Java 7中的钻石操作员可以减less烦人的makePair 。 有一些工作,使这真的很好:generics,但它应该是好的,现在。 (比较PECS)
  • hashcodeequals是由eclipse生成的。
  • cast方法中的编译时间是可以的,但似乎不太正确。
  • 我不确定在isInstance是否需要通配符。
  • 我刚刚写了这个回应评论,仅用于说明的目的。

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

 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; 

当我用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;} } 

我知道这有点难看,但是它可以工作,而且你只需要定义一次你的元组types。 元组是Java真正缺乏的东西。

编辑:大卫哈纳克的例子是更优雅的,因为它避免了定义getter,仍然保持对象不变。

我们应该忘记小效率,大约97%的时间:不成熟的优化是万恶之源。

D. Knuth

在Java 5之前,我会同意Map解决scheme不理想。 它不会给你编译时间types检查,所以可能在运行时造成问题。 但是,对于Java 5,我们有Generic Types。

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

 public Map<String, MyType> doStuff(); 

MyType当然是你正在返回的对象的types。

基本上我认为在这种情况下返回一个Map是正确的解决scheme,因为这正是你想要返回的 – 一个string映射到一个对象。

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

 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)使用Apache 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()); } 

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

有了你的特定问题,它会看起来像这样:

 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 ); } } 

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

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

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

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

 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); 

优点(与其他解决scheme相比):

  • 您不必为单个方法及其返回types创build特殊的类声明
  • 参数获得一个名称,因此在查看方法签名时更容易区分
  • 为每个参数input安全性

使用以下条目对象示例:

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

所有可能的解决scheme将是一个kludge(像容器对象,你的HashMap的想法,通过数组实现“多个返回值”)。 我build议从返回的列表中重新生成逗号分隔列表。 代码将变得更清洁。

Apache Commons有这样的元组和三元组:

  • ImmutablePair<L,R>一个由两个Object元素组成的不可变对。
  • ImmutableTriple<L,M,R>由三个对象元素组成的不可变三元组。
  • MutablePair<L,R>由两个对象元素组成的可变对。
  • MutableTriple<L,M,R>由三个对象元素组成的可变三元组。
  • Pair<L,R>由两个元素组成的一对。
  • Triple<L,M,R>由三个元素组成的三元组。

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

正如我所看到的,这里真的有三种select,解决scheme取决于上下文。 您可以select在生成列表的方法中实现名称的构造。 这是你select的select,但我不认为这是最好的select。 您正在生产者方法中创build耦合到不需要存在的消费方法。 其他呼叫者可能不需要额外的信息,您将计算这些呼叫者的额外信息。

或者,您可以让调用方法计算名称。 如果只有一个呼叫者需要这个信息,你可以在那里停下来。 你没有额外的依赖关系,虽然有一些额外的计算涉及,你已经避免使你的build设方法太具体。 这是一个很好的折衷。

最后,你可以让列表本身负责创build名称。 如果计算需要由多个调用者完成,则这是我要走的路线。 我认为这会把名称的创build与与对象本身关系最密切的类别放在一起。

在后一种情况下,我的解决scheme是创build一个专门的List类,它返回一个包含对象名称的逗号分隔的string。 让这个类足够聪明,它可以随着对象的添加和删除而dynamic地构造名称string。 然后返回该列表的一个实例,并根据需要调用名称生成方法。 尽pipe简单地延迟计算名称直到第一次调用方法并将其存储(延迟加载)可能几乎同样有效(并且更简单)。 如果添加/删除对象,则只需删除计算的值,并在下次调用时重新计算。

在dynamic语言(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); 

我采用了类似的方法,而不是根据我的要求进行一些调整,基本上我创build了以下类(以防万一,所有的东西都是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!"); } } 

在C ++(STL)中,有一对捆绑两个对象的类。 在Javagenerics中,一个对类是不可用的,虽然有一些需求 。 你可以很容易地自己实现它。

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

为什么不创build一个包含结果的WhateverFunctionResult对象, 以及parsing这些结果所需的逻辑,迭代等。在我看来,要么:

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

我看到这种问题一再出现。 不要害怕创build自己的包含数据和相关function的容器/结果类来处理这个问题。 如果你只是在HashMap或类似的东西中传递这些东西,那么你的客户必须把这张地图分开,并在每次他们想要使用结果的时候挖掘这些内容。

 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]); } } 

我一直在使用一个非常基本的方法来处理多重回报的问题。 它达到了目的,避免了复杂性。

我称之为string分隔方法

它是有效的,因为它甚至可以返回多种types的值,如int,double,char,string等

在这个方法中,我们使用了一个不太可能发生的string。 我们称之为分隔符。 当在一个函数中使用这个分隔符将被用来分隔不同的值

例如,我们将最后的返回作为(例如)intValue分隔符doubleValue分隔符…然后使用这个string,我们将检索所有需要的信息,也可以是不同types的

下面的代码将显示这个概念的工作

使用的分隔符是!@#,并且正在返回3个值intVal,doubleVal和stringVal

  public class TestMultipleReturns { public static String multipleVals() { String result = ""; String separator = "!@#"; int intVal = 5; // Code to process intVal double doubleVal = 3.14; // Code to process doubleVal String stringVal = "hello"; // Code to process Int intVal result = intVal + separator + doubleVal + separator + stringVal + separator; return (result); } public static void main(String[] args) { String res = multipleVals(); int intVal = Integer.parseInt(res.split("!@#")[0]); // Code to process intVal double doubleVal = Double.parseDouble(res.split("!@#")[1]); // Code to process doubleVal String stringVal = res.split("!@#")[2]; System.out.println(intVal+"\n"+doubleVal+"\n"+stringVal); } } 

OUTPUT

 5 3.14 hello BUILD SUCCESSFUL (total time: 2 seconds) 

将一个列表传递给你的方法并填充它,然后用名字返回String,如下所示:

 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); 

这不是完全回答这个问题,但是因为这里给出的每个解决scheme都有一些缺点,所以我build议尝试重构一下你的代码,所以你只需要返回一个值。

案例一。

你需要内部和外部的方法。 为什么不把它计算出来并传递给方法呢?

代替:

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

尝试:

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

这应该覆盖你的大部分需求,因为在大多数情况下,一个值创build在另一个之前,你可以用两种方法分割创build它们。 缺点是方法createThingsB有一个额外的参数比较createThings ,并且可能你传递完全相同的参数列表两次到不同的方法。


案例二

有史以来最明显的解决scheme和案例之一的简化​​版本。 这并不总是可能的,但也许这两个值可以独立创build?

代替:

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

而在你的情况下, 评论可能是一个好的方法,在Android中,你可以使用Pair 。 只是

 return Pair(yourList, yourCommaSeparatedValues); 

保持简单,并创build一个多个结果的情况下的类。 本示例接受来自数据库帮助程序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; } 

}

在C中,您可以通过将结果的指针作为parameter passing给结果来做到这一点:

 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); 

通过一个方法,人口普及……

public void buildResponse(String data,Map response);