如何从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生锈:
-
a
和b
都是不变的。 -
makePair
静态方法可以帮助您进行锅炉板打字,Java 7中的钻石操作员可以减less烦人的makePair
。 有一些工作,使这真的很好:generics,但它应该是好的,现在。 (比较PECS) -
hashcode
和equals
是由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这些结果所需的逻辑,迭代等。在我看来,要么:
- 这些结果对象是紧密联系在一起/相关的并且属于一起,或者:
- 他们是不相关的,在这种情况下,你的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);