varias valores valor sentencia retornar retornan retorna que para objeto metodos metodo java return

valores - ¿Cómo devolver múltiples objetos desde un método Java?



retornar un valor en c# (25)

Deberíamos olvidarnos de las pequeñas eficiencias, digamos aproximadamente el 97% del tiempo: la optimización prematura es la raíz de todo mal.

D. Knuth

Quiero devolver dos objetos de un método Java y me preguntaba ¿cuál podría ser una buena forma de hacerlo?

Las posibles maneras en que puedo pensar son: devolver un HashMap (ya que los dos Objetos están relacionados) o devolver un ArrayList de objetos.

Para ser más precisos, los dos objetos que deseo devolver son (a) List de objetos y (b) nombres separados por comas de los mismos.

Quiero devolver estos dos Objetos de un método porque no quiero repetir la lista de objetos para obtener los nombres separados por comas (lo cual puedo hacer en el mismo ciclo en este método).

De alguna manera, devolver un HashMap no parece una forma muy elegante de hacerlo.


¿Por qué no crear un objeto WhateverFunctionResult que contenga los resultados, y la lógica necesaria para analizar estos resultados, repetir sobre ellos, etc. Me parece que:

  1. Estos objetos de resultados están íntimamente relacionados / relacionados y pertenecen juntos, o:
  2. no están relacionados, en cuyo caso su función no está bien definida en términos de lo que está tratando de hacer (es decir, hacer dos cosas diferentes)

Veo este tipo de problema surgir una y otra vez. No tenga miedo de crear sus propias clases de contenedor / resultado que contengan los datos y la funcionalidad asociada para manejar esto. Si simplemente pasa el material en un HashMap o similar, entonces sus clientes tienen que separar este mapa y asimilar los contenidos cada vez que quieran usar los resultados.


Alternativamente, en situaciones en las que deseo devolver varias cosas de un método, algunas veces usaré un mecanismo de devolución de llamada en lugar de un contenedor. Esto funciona muy bien en situaciones en las que no puedo especificar con anticipación cuántos objetos se generarán.

Con su problema particular, se vería algo como esto:

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


Antes de Java 5, estaría de acuerdo en que la solución Map no es ideal. No le daría la verificación de tipo de tiempo de compilación, por lo que puede causar problemas en el tiempo de ejecución. Sin embargo, con Java 5, tenemos tipos genéricos.

Entonces su método podría verse así:

public Map<String, MyType> doStuff();

MyType, por supuesto, es el tipo de objeto que estás devolviendo.

Básicamente, creo que devolver un mapa es la solución correcta en este caso, porque eso es exactamente lo que desea devolver: una asignación de una cadena a un objeto.


Apache Commons tiene tupla y triple para esto:

  • ImmutablePair<L,R> Un par inmutable que consta de dos elementos Object.
  • ImmutableTriple<L,M,R> Un triple inmutable que consta de tres elementos Object.
  • MutablePair<L,R> Un par mutable que consta de dos elementos Object.
  • MutableTriple<L,M,R> Un triple mutable que consta de tres elementos Object.
  • Pair<L,R> Un par que consta de dos elementos.
  • Triple<L,M,R> Un triple que consta de tres elementos.

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


Casi siempre termino definiendo clases n-Tuple cuando codigo en Java. Por ejemplo:

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

Sé que es un poco feo, pero funciona, y solo tienes que definir tus tipos de tuplas una vez. Las tuplas son algo de lo que Java realmente carece.

EDITAR: El ejemplo de David Hanak es más elegante, ya que evita definir getters y aún mantiene el objeto inmutable.


Como yo lo veo, en realidad hay tres opciones aquí y la solución depende del contexto. Puede optar por implementar la construcción del nombre en el método que produce la lista. Esta es la elección que ha elegido, pero no creo que sea la mejor. Está creando un acoplamiento en el método productor con el método consumidor que no necesita existir. Es posible que otras personas que llaman no necesiten la información adicional y que usted esté calculando información adicional para estas personas.

Alternativamente, puede hacer que el método de llamada calcule el nombre. Si solo hay una persona que llama que necesita esta información, puede detenerse allí. No tiene dependencias adicionales y aunque hay un pequeño cálculo adicional involucrado, ha evitado que su método de construcción sea demasiado específico. Esta es una buena solución de compromiso.

Por último, podría hacer que la lista sea responsable de crear el nombre. Esta es la ruta que seguiría si el cálculo necesita ser realizado por más de un llamante. Creo que esto pone la responsabilidad de la creación de los nombres con la clase que está más estrechamente relacionada con los objetos mismos.

En este último caso, mi solución sería crear una clase Lista especializada que devuelva una cadena separada por comas de los nombres de los objetos que contiene. Haga que la clase sea lo suficientemente inteligente como para construir la cadena de nombre sobre la marcha a medida que los objetos se agregan y eliminan. A continuación, devuelva una instancia de esta lista y llame al método de generación de nombre según sea necesario. Aunque puede ser casi tan eficiente (y más simple) simplemente retrasar el cálculo de los nombres hasta la primera vez que se llama al método y almacenarlo luego (carga diferida). Si agrega / elimina un objeto, solo necesita eliminar el valor calculado y hacer que se vuelva a calcular en la próxima llamada.


En C ++ (STL) hay una clase de par para agrupar dos objetos. En Java Generics, una clase de dos no está disponible, aunque existe cierta demand . Sin embargo, usted mismo podría implementarlo fácilmente.

Sin embargo, estoy de acuerdo con algunas otras respuestas en que si necesita devolver dos o más objetos de un método, sería mejor encapsularlos en una clase.


En C, lo haría pasando punteros a marcadores de posición para los resultados como argumentos:

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

Probemos algo similar, en 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);


En cuanto al problema de los valores de rendimiento múltiples en general, generalmente utilizo una pequeña clase auxiliar que envuelve un único valor de retorno y se pasa como parámetro del método:

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

(para los tipos de datos primitivos uso variaciones menores para almacenar directamente el valor)

Un método que quiere devolver valores múltiples se declararía de la siguiente manera:

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

Quizás la principal desventaja es que quien llama debe preparar los objetos devueltos por adelantado en caso de que quiera usarlos (y el método debe verificar si hay punteros nulos).

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

Ventajas (en comparación con otras soluciones propuestas):

  • No es necesario crear una declaración de clase especial para los métodos individuales y sus tipos de devolución
  • Los parámetros obtienen un nombre y, por lo tanto, son más fáciles de diferenciar cuando se mira la firma del método
  • Escriba seguridad para cada parámetro

En el caso de que el método que está llamando sea privado o se llame desde una ubicación, intente

return new Object[]{value1, value2};

La persona que llama se ve así:

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

El ejemplo de par de David Hanak no tiene ningún beneficio sintáctico, y está limitado a dos valores.

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

Y la persona que llama se ve así:

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


Esto no responde exactamente a la pregunta, pero como cada una de las soluciones que aquí se presentan tiene algunos inconvenientes, sugiero que intentemos refactorizar el código un poco para que solo tenga que devolver un valor.

Caso uno.

Necesitas algo dentro y fuera de tu método. ¿Por qué no calcularlo afuera y pasarlo al método?

En lugar de:

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

Tratar:

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

Esto debería cubrir la mayoría de tus necesidades, ya que en la mayoría de las situaciones se crea un valor antes que el otro y puedes dividirlos creando dos métodos. El inconveniente es que el método createThingsB tiene un parámetro adicional en comparación con createThings , y posiblemente esté pasando exactamente la misma lista de parámetros dos veces a diferentes métodos.

Caso dos.

La solución más obvia de todos los tiempos y una versión simplificada del primer caso. No siempre es posible, pero tal vez ambos valores se pueden crear de forma independiente el uno del otro.

En lugar de:

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

Tratar:

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

Para hacerlo más útil, estos dos métodos pueden compartir alguna lógica común:

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


He estado usando un enfoque muy básico para enfrentar los problemas de los retornos múltiples. Sirve al propósito y evita la complejidad.

Yo lo llamo el método de separación de cuerdas

Y es eficaz, ya que incluso puede devolver valores de varios tipos, por ejemplo, int, double, char, string, etc.

En este enfoque hacemos un uso de una cadena que es muy poco probable que ocurra en general. Lo llamamos como un separador. Este separador se usaría para separar varios valores cuando se usa en una función

Por ejemplo, tendremos nuestro retorno final como (por ejemplo) intValue separator doubleValue separator ... Y luego, utilizando esta cadena, recuperaremos toda la información requerida, que también puede ser de diferentes tipos.

El siguiente código mostrará el funcionamiento de este concepto

El separador utilizado es ! @ # Y se devuelven 3 valores intVal, doubleVal y 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); } }

SALIDA

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


Mantenlo simple y crea una clase para situaciones de resultados múltiples. Este ejemplo acepta una ArrayList y un texto de mensaje de un databasehelper getInfo.

Donde llama a la rutina que devuelve múltiples valores codifica:

multResult res = mydb.getInfo();

En la rutina getInfo, usted codifica:

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

y define una clase multResult con:

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

}


Mientras que en su caso, el comentario puede ser una buena forma de hacerlo, en Android, puede usar Pair . Simplemente

return new Pair<>(yourList, yourCommaSeparatedValues);


PASAR UN HASH EN EL MÉTODO Y POBLARLO ......

public void buildResponse (datos de cadena, respuesta de mapa);


Pase una lista a su método y rellene, luego devuelva la cadena con los nombres, como esta:

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

Entonces llámalo así:

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


Puede hacer algo como una tupla en lenguaje dinámico (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]); } }

y usar así

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


Puede usar cualquiera de las siguientes formas:

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) Usando Matriz

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) Usando 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) Usando 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) Usando su clase de contenedor personalizado

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) Usando 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) Usando el par de 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()); }


Seguí un enfoque similar al descrito en las otras respuestas con algunos ajustes basados ​​en el requisito que tenía, básicamente creé las siguientes clases (por si acaso, todo es 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); } }

Entonces, mi requisito era simple, en la clase de repositorio que llega al DB, para obtener métodos que recuperar datos de la base de datos, necesito verificar si falló o si tuve éxito, entonces, si tenía éxito, tenía que jugar con la lista de retorno. , si falla, detenga la ejecución y notifique el error.

Entonces, por ejemplo, mis métodos son así:

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 }

Donde ResultMessage es solo una clase con dos campos (código / mensaje) y Cliente es cualquier clase con un grupo de campos que proviene de la base de datos.

Luego, para verificar el resultado, simplemente hago esto:

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


Si desea devolver dos objetos, generalmente desea devolver un único objeto que encapsula los dos objetos.

Podría devolver una Lista de objetos NamedObject como este:

public class NamedObject<T> { public final String name; public final T object; public NamedObject(String name, T object) { this.name = name; this.object = object; } }

A continuación, puede devolver fácilmente una List<NamedObject<WhateverTypeYouWant>> .

Además: ¿Por qué le gustaría devolver una lista de nombres separados por comas en lugar de una List<String> ? O mejor aún, devuelva un Map<String,TheObjectType> con las claves que son los nombres y los valores de los objetos (a menos que los objetos tengan un orden especificado, en cuyo caso un NavigableMap podría ser lo que usted desea).


Si sabe que va a devolver dos objetos, también puede usar un par genérico:

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

Editar Una implementación más completa de lo anterior:

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

Notas, principalmente sobre la oxidación con Java y genéricos:

  • tanto a como b son inmutables.
  • makePair método estático makePair ayuda con el tipeo de la placa de la caldera, que el operador de diamante en Java 7 hará menos molesto. Hay algo de trabajo para hacer esto realmente bueno: genéricos, pero debería estar bien ahora. (cf PECS)
  • hashcode e equals son generados por eclipse.
  • el tiempo de compilación en el método de conversión está bien, pero no parece del todo correcto.
  • No estoy seguro de si los comodines en isInstance son necesarios.
  • Acabo de escribir esto en respuesta a los comentarios, solo con fines ilustrativos.

Todas las soluciones posibles serán un kludge (como objetos contenedores, su idea HashMap, "valores de retorno múltiples" como se realiza a través de matrices). Recomiendo regenerar la lista separada por comas de la Lista devuelta. El código terminará siendo mucho más limpio.


Uso del siguiente objeto de entrada Ejemplo:

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


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