Comment retourner plusieurs objects d’une méthode Java?

Je veux retourner deux objects d’une méthode Java et je me demandais ce qui pourrait être un bon moyen de le faire?

Les moyens possibles pour y penser sont les suivants: renvoyer une HashMap (puisque les deux objects sont liés) ou renvoyer une liste d’objects ArrayList .

Pour être plus précis, les deux objects que je veux renvoyer sont (a) List des objects et (b) noms séparés par des virgules.

Je veux renvoyer ces deux objects d’une méthode car je ne veux pas parcourir la liste des objects pour obtenir les noms séparés par des virgules (ce que je peux faire dans la même boucle dans cette méthode).

D’une manière ou d’une autre, le fait de retourner un HashMap ne semble pas être une manière très élégante de le faire.

Si vous souhaitez renvoyer deux objects, vous souhaitez généralement renvoyer un object unique qui encapsule les deux objects à la place.

Vous pouvez retourner une liste d’objects NamedObject comme ceci:

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

Ensuite, vous pouvez facilement retourner une List> .

Aussi: Pourquoi voudriez-vous renvoyer une liste de noms séparés par des virgules au lieu d’une List ? Ou mieux encore, renvoyez une Map avec les clés comme noms et les valeurs des objects (à moins que vos objects aient un ordre spécifié, auquel cas une NavigableMap peut être ce que vous voulez).

Si vous savez que vous allez retourner deux objects, vous pouvez également utiliser une paire générique:

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

Edit Une implémentation plus complète de ce qui précède:

 package util; public class Pair { public static  Pair makePair(P p, Q q) { return new Pair(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  Pair cast(Pair pair, Class

pClass, Class qClass) { if (pair.isInstance(pClass, qClass)) { return (Pair) pair; } throw new ClassCastException(); } }

Notes, principalement autour de la rouille avec Java et génériques:

  • a et b sont immuables.
  • makePair méthode statique makePair vous aide à taper la plaque chauffante, ce que l’opérateur diamant de Java 7 rendra moins gênant. Il y a du travail à faire pour que ce soit vraiment sympa: génériques, mais ça devrait être ok maintenant. (cf PECS)
  • hashcode et les hashcode sont générés par éclipse.
  • le temps de compilation dans la méthode cast est correct, mais ne semble pas correct.
  • Je ne suis pas sûr si les caractères génériques dans isInstance sont nécessaires.
  • Je viens d’écrire ceci en réponse à des commentaires, à des fins d’illustration seulement.

Si la méthode que vous appelez est privée ou appelée depuis un emplacement, essayez

 return new Object[]{value1, value2}; 

L’appelant ressemble à:

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

L’exemple de la paire par David Hanak n’a aucun avantage syntaxique et se limite à deux valeurs.

 return new Pair(value1, value2); 

Et l’appelant ressemble à:

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

Je finis presque toujours par définir des classes n-Tuple lorsque je code en Java. Par exemple:

 public class Tuple2 { 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;} } 

Je sais que c’est un peu moche, mais ça marche, et il vous suffit de définir vos types de tuple une fois. Les tuples sont quelque chose qui manque vraiment à Java.

EDIT: l’exemple de David Hanak est plus élégant, car il évite de définir les getters et garde toujours l’immuable.

Vous pouvez utiliser l’un des moyens suivants:

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

1) Utilisation du tableau

 private static Ssortingng[] methodWithArrayResult() { //... return new Ssortingng[]{"valueA", "valueB"}; } private static void usingArrayResultTest() { Ssortingng[] result = methodWithArrayResult(); System.out.println(); System.out.println("A = " + result[VALUE_A]); System.out.println("B = " + result[VALUE_B]); } 

2) Utilisation de ArrayList

 private static List methodWithListResult() { //... return Arrays.asList("valueA", "valueB"); } private static void usingListResultTest() { List result = methodWithListResult(); System.out.println(); System.out.println("A = " + result.get(VALUE_A)); System.out.println("B = " + result.get(VALUE_B)); } 

3) Utilisation de HashMap

 private static Map methodWithMapResult() { Map result = new HashMap<>(RETURN_COUNT); result.put(A, "valueA"); result.put(B, "valueB"); //... return result; } private static void usingMapResultTest() { Map result = methodWithMapResult(); System.out.println(); System.out.println("A = " + result.get(A)); System.out.println("B = " + result.get(B)); } 

4) Utilisation de votre classe de conteneur personnalisée

 private static class MyContainer { 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, toSsortingng if need } private static MyContainer methodWithContainerResult() { //... return new MyContainer("valueA", "valueB"); } private static void usingContainerResultTest() { MyContainer result = methodWithContainerResult(); System.out.println(); System.out.println("A = " + result.getFirst()); System.out.println("B = " + result.getSecond()); } 

5) Utilisation de AbstractMap.simpleEntry

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

6) Utilisation d’une paire d’ Apache Commons

 private static Pair methodWithPairResult() { //... return new ImmutablePair<>("valueA", "valueB"); } private static void usingPairResultTest() { Pair result = methodWithPairResult(); System.out.println(); System.out.println("A = " + result.getKey()); System.out.println("B = " + result.getValue()); } 

Nous devrions oublier les petites efficacités, disons environ 97% du temps: l’optimisation prématurée est la racine de tous les maux.

D. Knuth

Avant Java 5, je conviens que la solution Map n’est pas idéale. Cela ne vous donnerait pas la vérification du type de compilation, cela peut causer des problèmes à l’exécution. Cependant, avec Java 5, nous avons des types génériques.

Donc, votre méthode pourrait ressembler à ceci:

 public Map doStuff(); 

MyType bien sûr étant le type d’object que vous retournez.

Fondamentalement, je pense que renvoyer une carte est la bonne solution dans ce cas, car c’est exactement ce que vous voulez retourner: une correspondance entre une chaîne et un object.

Sinon, dans des situations où je souhaite renvoyer un certain nombre de choses à partir d’une méthode, j’utiliserai parfois un mécanisme de rappel au lieu d’un conteneur. Cela fonctionne très bien dans des situations où je ne peux pas spécifier à l’avance combien d’objects seront générés.

Avec votre problème particulier, cela ressemblerait à ceci:

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

En ce qui concerne le problème des valeurs de retour multiples en général, j’utilise généralement une petite classe d’assistance qui encapsule une seule valeur de retour et est passée en paramètre à la méthode:

 public class ReturnParameter { 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; } } 

(pour les types de données primitifs, j’utilise des variations mineures pour stocker directement la valeur)

Une méthode qui veut renvoyer plusieurs valeurs serait alors déclarée comme suit:

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

L’inconvénient majeur est peut-être que l’appelant doit préparer les objects de retour à l’avance au cas où il voudrait les utiliser (et que la méthode devrait vérifier s’il existe des pointeurs nuls).

 ReturnParameter nameForFirstValue = new ReturnParameter(); ReturnParameter nameForSecondValue = new ReturnParameter(); methodThatReturnsTwoValues(nameForFirstValue, nameForSecondValue); 

Avantages (par rapport aux autres solutions proposées):

  • Vous n’avez pas besoin de créer une déclaration de classe spéciale pour les méthodes individuelles et leurs types de retour
  • Les parameters ont un nom et sont donc plus faciles à différencier lorsque l’on regarde la signature de la méthode
  • Type de sécurité pour chaque paramètre

Utilisation de l’object d’entrée suivant Exemple:

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

Toutes les solutions possibles seront un kludge (comme les objects conteneur, votre idée HashMap, les «valeurs de retour multiples» telles qu’elles sont réalisées via des tableaux). Je recommande de régénérer la liste séparée par des virgules de la liste renvoyée. Le code finira par être beaucoup plus propre.

Apache Commons a tuple et sortingple pour cela:

  • ImmutablePair Paire immuable constituée de deux éléments Object.
  • ImmutableTriple Un sortingplet immuable composé de trois éléments Object.
  • MutablePair Paire mutable composée de deux éléments Object.
  • MutableTriple Un sortingple mutable composé de trois éléments Object.
  • Pair Une paire composée de deux éléments.
  • Triple Un sortingple composé de trois éléments.

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

Alors que dans votre cas, le commentaire peut être un bon moyen d’aller, dans Android, vous pouvez utiliser Pair . Simplement

 return new Pair<>(yourList, yourCommaSeparatedValues); 

À mon avis, il y a vraiment trois choix ici et la solution dépend du contexte. Vous pouvez choisir d’implémenter la construction du nom dans la méthode qui produit la liste. C’est le choix que vous avez choisi, mais je ne pense pas que ce soit le meilleur. Vous créez un couplage dans la méthode producteur avec la méthode de consommation qui n’a pas besoin d’exister. D’autres appelants peuvent ne pas avoir besoin d’informations supplémentaires et vous calculez des informations supplémentaires pour ces appelants.

Vous pouvez également demander à la méthode appelante de calculer le nom. Si un seul appelant a besoin de ces informations, vous pouvez vous arrêter là. Vous n’avez pas de dépendances supplémentaires et bien qu’il y ait un petit calcul supplémentaire, vous avez évité de rendre votre méthode de construction trop spécifique. C’est un bon compromis.

Enfin, vous pourriez avoir la liste elle-même responsable de la création du nom. C’est la voie que j’irais si le calcul devait être effectué par plusieurs appelants. Je pense que cela place la responsabilité de la création des noms dans la classe la plus proche des objects eux-mêmes.

Dans ce dernier cas, ma solution serait de créer une classe List spécialisée qui renvoie une chaîne séparée par des virgules des noms des objects qu’elle contient. Rendez la classe suffisamment intelligente pour créer la chaîne de nom à la volée, au fur et à mesure que des objects sont ajoutés et supprimés. Ensuite, retournez une instance de cette liste et appelez la méthode de génération de nom si nécessaire. Bien qu’il soit presque aussi efficace (et plus simple) de simplement retarder le calcul des noms jusqu’à la première fois que la méthode est appelée et de la stocker ensuite (chargement différé). Si vous ajoutez / supprimez un object, il vous suffit de supprimer la valeur calculée et de la faire recalculer lors du prochain appel.

Peut faire quelque chose comme un tuple en langage 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 at(int index, Class someClass) { return someClass.cast(multiReturns[index]); } } 

et utiliser comme ça

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

J’ai suivi une approche similaire à celle décrite dans les autres réponses avec quelques modifications basées sur l’exigence que j’avais, essentiellement j’ai créé les classes suivantes (juste au cas où, tout est Java):

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

Ensuite, mon exigence était simple: dans la classe de référentiel qui atteint la firebase database, pour les méthodes de récupération plutôt que pour extraire des données de la firebase database, je dois vérifier si cela a échoué ou réussi. , en cas d’échec, arrêtez l’exécution et notifiez l’erreur.

Ainsi, par exemple, mes méthodes sont comme ceci:

 public Pair> getCustomers() { List list = new ArrayList(); 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 } 

Où ResultMessage est juste une classe avec deux champs (code / message) et le client est une classe avec un groupe de champs provenant de la firebase database.

Ensuite, pour vérifier le résultat, je fais juste ceci:

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

Restez simple et créez une classe pour plusieurs situations. Cet exemple accepte une ArrayList et un texte de message provenant d’un gestionnaire de firebase database getInfo.

Vous appelez la routine qui renvoie plusieurs valeurs que vous codez:

 multResult res = mydb.getInfo(); 

Dans la routine getInfo vous codez:

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

et définir une classe multResult avec:

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

}

En C ++ (STL), il existe une classe de paires pour regrouper deux objects. Dans Java Generics, une classe de paires n’est pas disponible, même si elle est demandée . Vous pourriez facilement l’implémenter vous-même.

Je suis cependant d’accord avec certaines autres réponses que si vous devez renvoyer plusieurs objects d’une méthode, il serait préférable de les encapsuler dans une classe.

Pourquoi ne pas créer un object WhateverFunctionResult contenant vos résultats et la logique nécessaire pour parsingr ces résultats, itérer ensuite etc. Il me semble que:

  1. Ces objects de résultat sont intimement liés / liés et appartiennent ensemble, ou:
  2. ils ne sont pas liés, auquel cas votre fonction n’est pas bien définie en termes de ce qu’elle essaie de faire (c.-à-d. faire deux choses différentes)

Je vois ce genre de problème surgir encore et encore. N’ayez pas peur de créer vos propres classes conteneur / résultat contenant les données et les fonctionnalités associées pour gérer cela. Si vous transmettez simplement les données dans un HashMap ou similaire, vos clients doivent séparer cette carte et en saisir le contenu chaque fois qu’ils souhaitent utiliser les résultats.

 public class MultipleReturnValues { public MultipleReturnValues() { } public static void functionWithSeveralReturnValues(final Ssortingng[] returnValues) { returnValues[0] = "return value 1"; returnValues[1] = "return value 2"; } public static void main(Ssortingng[] args) { Ssortingng[] returnValues = new Ssortingng[2]; functionWithSeveralReturnValues(returnValues); System.out.println("returnValues[0] = " + returnValues[0]); System.out.println("returnValues[1] = " + returnValues[1]); } } 

Cela ne répond pas exactement à la question, mais comme chacune des solutions proposées présente certains inconvénients, je suggère d’essayer de refactoriser un peu votre code afin de ne renvoyer qu’une seule valeur.

Premier cas

Vous avez besoin de quelque chose à l’intérieur et à l’extérieur de votre méthode. Pourquoi ne pas le calculer à l’extérieur et le transmettre à la méthode?

Au lieu de:

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

Essayer:

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

Cela devrait couvrir la plupart de vos besoins, car dans la plupart des cas, une valeur est créée avant l’autre et vous pouvez les diviser en deux méthodes. L’inconvénient est que la méthode createThingsB a un paramètre supplémentaire comparé à createThings , et vous transmettez peut-être exactement la même liste de parameters deux fois à des méthodes différentes.


Deuxième cas

La solution la plus évidente et une version simplifiée de la première. Ce n’est pas toujours possible, mais peut-être que les deux valeurs peuvent être créées indépendamment l’une de l’autre?

Au lieu de:

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

Essayer:

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

Pour le rendre plus utile, ces deux méthodes peuvent partager une logique commune:

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

Passer une liste à votre méthode et la remplir, puis renvoyer la chaîne avec les noms, comme ceci:

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

Alors appelez comme ceci:

 List values = new ArrayList(); Ssortingng names = buildList(values); 

J’ai utilisé une approche très simple pour traiter les problèmes de retours multiples. Cela sert le but et évite la complexité.

Je l’appelle le séparateur de chaînes

Et il est efficace car il peut même renvoyer des valeurs de plusieurs types, par exemple int, double, char, ssortingng, etc.

Dans cette approche, nous utilisons une chaîne qui est très improbable. Nous l’appelons comme un séparateur. Ce séparateur serait utilisé pour séparer différentes valeurs lorsqu’il est utilisé dans une fonction

Par exemple, nous aurons notre retour final en tant que (par exemple) séparateur intValue séparateur doubleValue … Et puis, en utilisant cette chaîne, nous récupérerons toutes les informations requirejses, qui peuvent également être de types différents.

Le code suivant affichera le fonctionnement de ce concept

Le séparateur utilisé est ! @ # Et 3 valeurs sont retournées intVal, doubleVal et ssortingngVal

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

SORTIE

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

En C, vous le feriez en passant des pointeurs aux espaces réservés pour les résultats en tant qu’arguments:

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

Essayons quelque chose de similaire, en Java.

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

PASSER UN HASH DANS LA MÉTHODE ET POPULER IT ……

public void buildResponse (données de chaîne, réponse de carte);