188 votes

Comment renvoyer plusieurs objets à partir d'une méthode Java ?

Je veux renvoyer deux objets à partir d'une méthode Java et je me demandais quelle serait la meilleure façon de le faire ?

Les méthodes possibles auxquelles je pense sont les suivantes : retourner une HashMap (puisque les deux Objets sont liés) ou retourner un ArrayList de Object objets.

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

Je veux renvoyer ces deux objets à partir d'une seule méthode car je ne veux pas itérer dans la liste des objets 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, renvoyer un HashMap ne semble pas être un moyen très élégant de le faire.

1 votes

La liste et les CSV sont-ils des vues différentes des mêmes données ? On dirait que ce dont vous avez besoin est un objet où vous avez un unique List et une sorte de table de consultation.

0 votes

137voto

Joachim Sauer Points 133411

Si vous souhaitez renvoyer deux objets, il est généralement préférable de renvoyer un objet unique qui encapsule les deux objets.

Vous pourriez renvoyer une liste de NamedObject des objets comme celui-ci :

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

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

Vous pouvez alors facilement renvoyer un List<NamedObject<WhateverTypeYouWant>> .

Aussi : Pourquoi vouloir retourner une liste de noms séparés par des virgules au lieu d'une List<String> ? Ou mieux encore, renvoyer un Map<String,TheObjectType> dont les clés sont les noms et les valeurs les objets (sauf si vos objets ont un ordre spécifique, auquel cas un fichier NavigableMap pourrait être ce que vous voulez.

0 votes

La raison pour laquelle la liste est séparée par des virgules est la suivante : Si je ne crée pas la liste ici, je devrais le faire dans l'appelant en parcourant les objets en boucle (la valeur CS est nécessaire). Peut-être que je pré-optimise inutilement.

3 votes

Je me suis toujours demandé pourquoi Java n'avait pas de classe Pair<T, U> pour cette raison.

0 votes

Jagmal : oui, si la seule raison de renvoyer la liste séparée par des virgules est cette optimisation, alors oubliez-la.

73voto

David Hanak Points 5960

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

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

Modifier Une mise en œuvre plus complète de ce qui précède :

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

    }

}

Notes, principalement sur la rouille de Java et les génériques :

  • les deux a et b sont immuables.
  • makePair La méthode statique vous aide avec le typage de la plaque de chaudière, que l'opérateur diamant dans Java 7 rendra moins ennuyeux. Il y a encore du travail à faire pour que ce soit vraiment agréable en ce qui concerne les génériques, mais ça devrait être bon maintenant. (c.f. PECS)
  • hashcode et equals sont générés par eclipse.
  • le moulage au moment de la compilation dans le cast La méthode est bonne, mais ne semble pas tout à fait correcte.
  • Je ne suis pas sûr que les caractères de remplacement dans isInstance sont nécessaires.
  • Je viens d'écrire ceci en réponse à des commentaires, à titre d'illustration uniquement.

0 votes

J'ai généralement cette classe qui traîne dans chaque base de code sur laquelle je travaille. J'ajouterais également : une implémentation de hashCode/equals, et éventuellement une méthode statique isInstance() et cast().

0 votes

Bien sûr, il existe de nombreuses façons de rendre cette classe plus intelligente et plus pratique à utiliser. La version ci-dessus inclut ce qui est juste suffisant dans une déclaration unique.

0 votes

@jamesh : cela vous dérange-t-il d'écrire votre Pair en détail ici ? J'aimerais savoir à quoi elle ressemble après avoir fourni "une implémentation hashCode/equals, et éventuellement une méthode statique isInstance() et cast()". Merci.

29voto

Kyle Lahnakoski Points 386

Dans le cas où la méthode que vous appelez est privée, ou appelée d'un seul endroit, essayez de

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 Pair de David Hanak n'a aucun avantage syntaxique, et est limité à deux valeurs.

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

Et l'appelant ressemble à :

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

8 votes

La paire a un avantage de contrôle de type classe

5 votes

IMHO, n'allez pas dans ce sens - la déclaration en dit trop peu sur les valeurs de retour attendues. À ma connaissance, il est préférable de créer des classes génériques qui précisent combien de paramètres sont renvoyés et le type de ces paramètres. Pair<T1, T2> , Tuple<T1, T2, T3> , Tuple<T1, T2, T3, T4> etc. Ensuite, une utilisation spécifique indique le nombre et les types de paramètres Pair<int, String> temp = ... ou autre.

17voto

Ulrik Rasmussen Points 608

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

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

Je sais que c'est un peu laid, mais ça marche, et vous n'avez qu'à définir vos types de tuple une seule fois. Les tuples sont quelque chose dont Java manque vraiment.

EDIT : L'exemple de David Hanak est plus élégant, car il évite de définir des getters tout en gardant l'objet immuable.

10voto

gizmo Points 8528

Nous devrions oublier les petits gains d'efficacité, disons environ 97 % du temps : l'optimisation prématurée est la racine de tous les maux.

D. Knuth

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X