Wie kann ich mehrere Objekte von einer Java-Methode zurückgeben?

172

Ich möchte zwei Objekte von einer Java-Methode zurückgeben und habe mich gefragt, was ein guter Weg sein könnte, dies zu tun.

Die möglichen Möglichkeiten, die ich mir vorstellen kann, sind: Rückgabe eines HashMap(da die beiden Objekte miteinander verbunden sind) oder Rückgabe eines ArrayListvon ObjectObjekten.

Genauer gesagt sind die beiden Objekte, die ich zurückgeben möchte, (a) ListObjekte und (b) durch Kommas getrennte Namen derselben.

Ich möchte diese beiden Objekte von einer Methode zurückgeben, da ich die Liste der Objekte nicht durchlaufen möchte, um die durch Kommas getrennten Namen zu erhalten (was ich in dieser Methode in derselben Schleife tun kann).

Irgendwie HashMapsieht die Rückgabe von a nicht sehr elegant aus.

Jagmal
quelle
1
Unterscheiden sich die Liste und die CSVs grundsätzlich in denselben Ansichten? Es hört sich so an, als ob Sie ein Objekt benötigen, in dem Sie eine einzelne ListReferenz und eine Art Nachschlagetabelle haben.
James P.
Verwandte Frage: stackoverflow.com/questions/53453058/…
John McClane

Antworten:

128

Wenn Sie zwei Objekte zurückgeben möchten, möchten Sie normalerweise ein einzelnes Objekt zurückgeben, das stattdessen die beiden Objekte kapselt.

Sie können eine Liste von NamedObjectObjekten wie folgt zurückgeben:

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

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

Dann können Sie einfach a zurückgeben List<NamedObject<WhateverTypeYouWant>>.

Außerdem: Warum sollten Sie eine durch Kommas getrennte Liste von Namen anstelle von a zurückgeben List<String>? Oder noch besser, geben Sie a zurück, Map<String,TheObjectType>wobei die Schlüssel die Namen und Werte der Objekte sind (es sei denn, Ihre Objekte haben eine bestimmte Reihenfolge angegeben. In diesem Fall NavigableMapkönnte a das sein, was Sie möchten.

Joachim Sauer
quelle
Der Grund für die Rückgabe einer durch Kommas getrennten Liste ist: Wenn ich die Liste hier nicht erstelle, müsste ich dies im Aufrufer tun, indem ich die Objekte durchlaufe (CS-Wert wird benötigt). Vielleicht optimiere ich unnötig vor.
Jagmal
2
Ich habe mich immer gefragt, warum Java aus diesem Grund keine Pair <T, U> -Klasse hat.
David Koelle
Jagmal: Ja, wenn der einzige Grund für die Rückgabe der durch Kommas getrennten Liste diese Optimierung ist, dann vergessen Sie sie.
Joachim Sauer
Dies funktioniert nur dann gut, wenn die Elemente, die Sie zurückgeben möchten, derselben Klasse angehören oder zumindest einen gemeinsamen Vorfahren haben. Ich meine, die Verwendung von Object anstelle von WhateverTypeYouWant ist nicht sehr ordentlich.
David Hanak
@ David: Ich stimme zu, dass die Verwendung von Object hier nicht sehr ordentlich ist, aber Objekte ohne gemeinsamen Vorfahren zurückzugeben (außer natürlich Object) ist auch nicht sehr ordentlich. Ich würde sogar sagen, es ist ein Code-Geruch, wenn Sie das brauchen.
Joachim Sauer
69

Wenn Sie wissen, dass Sie zwei Objekte zurückgeben werden, können Sie auch ein generisches Paar verwenden:

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

Bearbeiten Eine vollständigere Implementierung der oben genannten:

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

    }

}

Anmerkungen, hauptsächlich zu Rost mit Java & Generika:

  • beide aund bsind unveränderlich.
  • makePairDie statische Methode hilft Ihnen bei der Eingabe von Kesselplatten, was der Diamantoperator in Java 7 weniger nervig macht. Es gibt einige Arbeiten, um dieses wirklich schöne Re: Generikum zu machen, aber es sollte jetzt in Ordnung sein. (vgl. PECS)
  • hashcodeund equalswerden durch Sonnenfinsternis erzeugt.
  • Das Casting der Kompilierungszeit in der castMethode ist in Ordnung, scheint aber nicht ganz richtig zu sein.
  • Ich bin mir nicht sicher, ob die Platzhalter isInstancenotwendig sind.
  • Ich habe dies nur als Antwort auf Kommentare geschrieben, nur zu Illustrationszwecken.
David Hanak
quelle
Normalerweise klopft diese Klasse in jeder Codebasis an, an der ich arbeite. Ich würde auch hinzufügen: eine hashCode / equals-Implementierung und möglicherweise eine statische isInstance () - und cast () -Methode.
Jamesh
Sicher, es gibt viele Möglichkeiten, diese Klasse intelligenter und bequemer zu gestalten. Die obige Version enthält, was gerade in einer One-Shot-Erklärung ausreicht.
David Hanak
@jamesh: Hast du etwas dagegen, dein Paar hier ausführlich zu schreiben? Ich möchte wissen, wie es aussieht, nachdem ich "eine hashCode / equals-Implementierung und möglicherweise eine statische isInstance () - und cast () -Methode" angegeben habe. Danke dir.
Qiang Li
@QiangLi - Normalerweise generiere ich die Hashcode & Equals-Methoden. Die Instanzmethode isInstance verwendet zwei Klassen und stellt sicher, dass die a & b der Instanz Instanzen dieser Klassen sind. Cast nimmt ein Paar <?,?> Und wirft es vorsichtig in ein Paar <A, B>. Die Implementierungen sollten ziemlich einfach sein (Hinweis: Class.cast (), Class.isInstance ())
Jamesh
2
Das ist eine sehr schöne PairImplementierung. Eine kleine Änderung, die ich vornehmen würde: Fügen Sie dem eine Nachricht hinzu ClassCastException. Andernfalls wird das Debuggen zum Albtraum, wenn dies aus irgendeinem Grund fehlschlägt. (und die Rohtypen-Unterdrückungswarnungen wären unnötig, wenn Sie sie umwandeln würden Pair<?,?>(was funktioniert, weil Sie nur ObjectMethoden von aund benötigen b). Stört es Sie, wenn ich den Code optimiere?
Joachim Sauer
25

Versuchen Sie es, falls die von Ihnen aufgerufene Methode privat ist oder von einem Ort aus aufgerufen wird

return new Object[]{value1, value2};

Der Anrufer sieht aus wie:

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

Das Pair-Beispiel von David Hanak hat keinen syntaktischen Vorteil und ist auf zwei Werte beschränkt.

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

Und der Anrufer sieht aus wie:

Pair<Type1, Type2> temp=myMethod(parameters);
Type1 value1=temp.a;  //For code clarity: temp.a is not descriptive
Type2 value2=temp.b;
Kyle Lahnakoski
quelle
7
Paar hat Klasse Typ Kontrolle Vorteil
Hlex
5
IMHO, gehen Sie nicht diesen Weg - die Deklaration sagt zu wenig über die erwarteten Rückgabewerte aus. AFAIK wird allgemeiner bevorzugt, generische Klassen zu erstellen, die angeben, wie viele Parameter zurückgegeben werden und welche Art von Parametern sie verwenden. Pair<T1, T2>, Tuple<T1, T2, T3>, Tuple<T1, T2, T3, T4>Usw. Dann eine spezifische Verwendung zeigt die Anzahl und Arten von Parametern Pair<int, String> temp = ...oder was auch immer.
ToolmakerSteve
22

Sie können eine der folgenden Möglichkeiten verwenden:

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) Array verwenden

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) Verwenden von 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) Verwenden von 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) Verwenden Ihrer benutzerdefinierten Containerklasse

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) Verwenden von 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) Mit Pair von 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());
}
Viacheslav Vedenin
quelle
16

Ich definiere fast immer n-Tupel-Klassen, wenn ich in Java codiere. Zum Beispiel:

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

Ich weiß, dass es ein bisschen hässlich ist, aber es funktioniert und Sie müssen Ihre Tupeltypen nur einmal definieren. Tupel fehlen Java wirklich.

BEARBEITEN: Das Beispiel von David Hanak ist eleganter, da es die Definition von Gettern vermeidet und das Objekt dennoch unveränderlich hält.

Ulrik Rasmussen
quelle
9

Vor Java 5 würde ich zustimmen, dass die Map-Lösung nicht ideal ist. Sie können den Typ der Kompilierungszeit nicht überprüfen, sodass zur Laufzeit Probleme auftreten können. Mit Java 5 haben wir jedoch generische Typen.

Ihre Methode könnte also so aussehen:

public Map<String, MyType> doStuff();

MyType ist natürlich der Objekttyp, den Sie zurückgeben.

Grundsätzlich denke ich, dass die Rückgabe einer Map in diesem Fall die richtige Lösung ist, da Sie genau das zurückgeben möchten - eine Zuordnung einer Zeichenfolge zu einem Objekt.

kipz
quelle
Dies funktioniert nicht, wenn einer der Namen kollidiert. Eine Liste kann Duplikate enthalten, eine Map jedoch nicht (enthält doppelte Schlüssel).
Tvanfosson
Natürlich. Ich machte Annahmen basierend auf der Frage - vielleicht unangemessen :)
kipz
Obwohl Ihre Annahme in diesem Fall zutrifft, gehe ich in den Bereich der vorzeitigen Optimierung (was ich nicht tun sollte).
Jagmal
6

Alternativ verwende ich in Situationen, in denen ich eine Reihe von Dingen von einer Methode zurückgeben möchte, manchmal einen Rückrufmechanismus anstelle eines Containers. Dies funktioniert sehr gut in Situationen, in denen ich nicht im Voraus angeben kann, wie viele Objekte generiert werden sollen.

Bei Ihrem speziellen Problem würde es ungefähr so ​​aussehen:

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

quelle
Es tut mir leid, dass Sie eine sehr alte Antwort von Ihnen kommentiert haben, aber wie verlaufen Rückrufe in Bezug auf die Speicherbereinigung? Ich habe sicherlich keine gute Speicherverwaltung Verständnis in java, wenn Sie bezwecken AAnruf - Objekt B.getResult()und B.getResult()Anrufe A.finishResult()als ein callback, wird das Objekt Bget Müll gesammelt oder es bis A endet nicht bleiben um ?? wahrscheinlich eine dumme Frage, aber es ist eine grundlegende Verwirrung, die ich habe!
Wired00
6

Apache Commons hat dafür Tupel und Triple:

  • ImmutablePair<L,R> Ein unveränderliches Paar, das aus zwei Objektelementen besteht.
  • ImmutableTriple<L,M,R> Ein unveränderliches Tripel bestehend aus drei Objektelementen.
  • MutablePair<L,R> Ein veränderliches Paar, das aus zwei Objektelementen besteht.
  • MutableTriple<L,M,R> Ein veränderliches Tripel, bestehend aus drei Objektelementen.
  • Pair<L,R> Ein Paar bestehend aus zwei Elementen.
  • Triple<L,M,R> Ein Triple bestehend aus drei Elementen.

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

Jacob
quelle
6

Während in Ihrem Fall der Kommentar ein guter Weg sein kann, können Sie ihn in Android verwenden Pair . Einfach

return new Pair<>(yourList, yourCommaSeparatedValues);
serv-inc
quelle
5

Verwendung des folgenden Eingabeobjekts Beispiel:

public Entry<A,B> methodname(arg)
{
.......

return new AbstractMap.simpleEntry<A,B>(instanceOfA,instanceOfB);
}
Sreeja Thummala
quelle
5

In Bezug auf das Problem mit mehreren Rückgabewerten im Allgemeinen verwende ich normalerweise eine kleine Hilfsklasse, die einen einzelnen Rückgabewert umschließt und als Parameter an die Methode übergeben wird:

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

(Für primitive Datentypen verwende ich geringfügige Variationen, um den Wert direkt zu speichern.)

Eine Methode, die mehrere Werte zurückgeben möchte, wird dann wie folgt deklariert:

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

Möglicherweise besteht der Hauptnachteil darin, dass der Aufrufer die Rückgabeobjekte im Voraus vorbereiten muss, falls er sie verwenden möchte (und die Methode sollte nach Nullzeigern suchen).

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

Vorteile (im Vergleich zu anderen vorgeschlagenen Lösungen):

  • Sie müssen keine spezielle Klassendeklaration für einzelne Methoden und deren Rückgabetypen erstellen
  • Die Parameter erhalten einen Namen und sind daher bei der Betrachtung der Methodensignatur leichter zu unterscheiden
  • Geben Sie für jeden Parameter Sicherheit ein
koma
quelle
Vielen Dank für eine Lösung, die jedem zurückgegebenen Wert Namen und Typensicherheit verleiht, ohne dass eine Klassendeklaration pro Satz zurückgegebener Werttypen erforderlich ist.
ToolmakerSteve
3

Alle möglichen Lösungen sind ein Kludge (wie Containerobjekte, Ihre HashMap-Idee, "mehrere Rückgabewerte", wie sie über Arrays realisiert werden). Ich empfehle, die durch Kommas getrennte Liste aus der zurückgegebenen Liste neu zu generieren. Der Code wird am Ende viel sauberer.

Bombe
quelle
Ich stimme Ihnen darin zu, aber wenn ich dies tue, werde ich am Ende zweimal eine Schleife durchlaufen (ich erstelle tatsächlich die Elemente der Liste einzeln in der vorhandenen Methode).
Jagmal
1
@Jagmal: Du kannst vielleicht zweimal eine Schleife machen, aber das spielt die meiste Zeit keine Rolle (siehe gizmos Antwort).
Joachim Sauer
1
Ja, versuchen Sie nicht, Ihren Code zu optimieren, es sei denn, Sie müssen es wirklich. Gizmo hat damit sehr recht.
Bombe
3

Halten Sie es einfach und erstellen Sie eine Klasse für Situationen mit mehreren Ergebnissen. In diesem Beispiel werden eine ArrayList und ein Nachrichtentext von einem Datenbankhelper getInfo akzeptiert.

Wo Sie die Routine aufrufen, die mehrere Werte zurückgibt, die Sie codieren:

multResult res = mydb.getInfo(); 

In der Routine getInfo codieren Sie:

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

und definieren Sie eine Klasse multResult mit:

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

}}

Martin
quelle
2

Aus meiner Sicht gibt es hier wirklich drei Möglichkeiten, und die Lösung hängt vom Kontext ab. Sie können die Konstruktion des Namens in der Methode implementieren, mit der die Liste erstellt wird. Dies ist die Wahl, die Sie gewählt haben, aber ich denke nicht, dass es die beste ist. Sie erstellen eine Kopplung in der Producer-Methode mit der Consuming-Methode, die nicht vorhanden sein muss. Andere Anrufer benötigen möglicherweise keine zusätzlichen Informationen, und Sie würden zusätzliche Informationen für diese Anrufer berechnen.

Alternativ können Sie die aufrufende Methode den Namen berechnen lassen. Wenn nur ein Anrufer diese Informationen benötigt, können Sie dort anhalten. Sie haben keine zusätzlichen Abhängigkeiten, und obwohl ein wenig zusätzliche Berechnung erforderlich ist, haben Sie vermieden, Ihre Konstruktionsmethode zu spezifisch zu gestalten. Dies ist ein guter Kompromiss.

Schließlich könnte die Liste selbst für die Erstellung des Namens verantwortlich sein. Dies ist der Weg, den ich gehen würde, wenn die Berechnung von mehr als einem Anrufer durchgeführt werden muss. Ich denke, dies überträgt die Verantwortung für die Erstellung der Namen mit der Klasse, die am engsten mit den Objekten selbst verwandt ist.

Im letzteren Fall würde meine Lösung darin bestehen, eine spezielle List-Klasse zu erstellen, die eine durch Kommas getrennte Zeichenfolge der Namen der darin enthaltenen Objekte zurückgibt. Machen Sie die Klasse so intelligent, dass sie die Namenszeichenfolge im laufenden Betrieb erstellt, wenn Objekte hinzugefügt und daraus entfernt werden. Geben Sie dann eine Instanz dieser Liste zurück und rufen Sie die Namensgenerierungsmethode nach Bedarf auf. Obwohl es fast genauso effizient (und einfacher) sein kann, die Berechnung der Namen einfach bis zum ersten Aufruf der Methode zu verzögern und dann zu speichern (verzögertes Laden). Wenn Sie ein Objekt hinzufügen / entfernen, müssen Sie nur den berechneten Wert entfernen und ihn beim nächsten Aufruf neu berechnen lassen.

Tvanfosson
quelle
2

Kann so etwas wie ein Tupel in dynamischer Sprache (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]);
}
}

und so verwenden

public Tuple returnMultiValues(){
   return Tuple._t(new ArrayList(),new HashMap())
}


Tuple t = returnMultiValues();
ArrayList list = t.at(0,ArrayList.class);
Deva
quelle
2

Ich verfolgte einen ähnlichen Ansatz wie in den anderen Antworten beschrieben mit ein paar Änderungen, basierend auf der Anforderung, die ich hatte. Im Grunde habe ich die folgenden Klassen erstellt (Nur für den Fall, dass alles Java ist):

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

Dann war meine Anforderung einfach: In der Repository-Klasse, die die Datenbank erreicht, muss ich für die Get-Methoden, um Daten aus der Datenbank abzurufen, prüfen, ob sie fehlgeschlagen sind oder erfolgreich sind. Wenn dies erfolgreich ist, muss ich mit der Rückgabeliste spielen Wenn dies fehlschlägt, stoppen Sie die Ausführung und benachrichtigen Sie den Fehler.

So sind meine Methoden zum Beispiel wie folgt:

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
}

Wobei ResultMessage nur eine Klasse mit zwei Feldern (Code / Nachricht) ist und Customer eine Klasse mit einer Reihe von Feldern ist, die aus der Datenbank stammen.

Um das Ergebnis zu überprüfen, mache ich einfach Folgendes:

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!");
    }
}
Marco Vargas
quelle
1

In C ++ (STL) gibt es eine Paarklasse zum Bündeln von zwei Objekten. In Java Generics ist keine Paarklasse verfügbar, obwohl eine gewisse Nachfrage besteht danach besteht. Sie können es jedoch leicht selbst implementieren.

Ich stimme jedoch einigen anderen Antworten zu, dass es besser ist, sie in einer Klasse zu kapseln, wenn Sie zwei oder mehr Objekte von einer Methode zurückgeben müssen.

kgiannakakis
quelle
1

Warum nicht ein WhateverFunctionResultObjekt erstellen , das Ihre Ergebnisse und die Logik enthält, die erforderlich ist, um diese Ergebnisse zu analysieren, dann zu wiederholen usw. Es scheint mir, dass entweder:

  1. Diese Ergebnisobjekte sind eng miteinander verbunden / verbunden und gehören zusammen oder oder:
  2. Sie haben nichts miteinander zu tun. In diesem Fall ist Ihre Funktion nicht genau definiert, was sie zu tun versucht (dh zwei verschiedene Dinge zu tun).

Ich sehe, dass diese Art von Problem immer wieder auftaucht. Haben Sie keine Angst, eigene Container- / Ergebnisklassen zu erstellen, die die Daten und die zugehörigen Funktionen enthalten, um dies zu handhaben. Wenn Sie das HashMapMaterial einfach in einem oder einem ähnlichen Format weitergeben, müssen Ihre Kunden diese Karte auseinander ziehen und den Inhalt jedes Mal durchsuchen, wenn sie die Ergebnisse verwenden möchten.

Brian Agnew
quelle
Weil es eine PITA ist, eine Klasse definieren zu müssen, wenn Sie mehrere Werte zurückgeben müssen, nur weil der Sprache diese allgemein nützliche Funktion fehlt;) Aber im Ernst, was Sie vorschlagen, ist es sehr oft wert, getan zu werden.
ToolmakerSteve
1
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]);
    }

}
tsug303
quelle
1

Dies beantwortet die Frage nicht genau, aber da jede der hier angegebenen Lösungen einige Nachteile aufweist, empfehle ich, Ihren Code ein wenig umzugestalten, sodass Sie nur einen Wert zurückgeben müssen.

Fall eins.

Sie benötigen sowohl innerhalb als auch außerhalb Ihrer Methode etwas. Warum nicht draußen berechnen und an die Methode übergeben?

Anstatt:

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

Versuchen:

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

Dies sollte die meisten Ihrer Anforderungen abdecken, da in den meisten Situationen ein Wert vor dem anderen erstellt wird und Sie die Erstellung in zwei Methoden aufteilen können. Der Nachteil ist, dass die Methode createThingsBim Vergleich zu einen zusätzlichen Parameter createThingshat und Sie möglicherweise genau dieselbe Parameterliste zweimal an verschiedene Methoden übergeben.


Fall zwei.

Die offensichtlichste Lösung aller Zeiten und eine vereinfachte Version von Fall eins. Es ist nicht immer möglich, aber vielleicht können beide Werte unabhängig voneinander erstellt werden?

Anstatt:

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

Versuchen:

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

Um es nützlicher zu machen, können diese beiden Methoden eine gemeinsame Logik haben:

public ThingA createThingA(...) {
    doCommonThings(); // common logic
    // create thing A
}
public ThingB createThingB(...) {
    doCommonThings(); // common logic
    // create thing B
}
X. Wo Satuk
quelle
0

Übergeben Sie eine Liste an Ihre Methode, füllen Sie sie aus und geben Sie den String mit den folgenden Namen zurück:

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

Dann nenne es so:

List<?> values = new ArrayList<?>();
String names = buildList(values);
Triqui
quelle
-2

Ich habe einen sehr einfachen Ansatz verwendet, um Probleme mit Mehrfachrenditen zu lösen. Es erfüllt den Zweck und vermeidet Komplexität.

Ich nenne es das String-Trennzeichen Approach

Und es ist effektiv, da es sogar Werte mehrerer Typen zurückgeben kann z. B. int, double, char, string usw.

Bei diesem Ansatz verwenden wir eine Zeichenfolge, die im Allgemeinen sehr unwahrscheinlich ist. Wir nennen es als Trennzeichen. Dieses Trennzeichen wird verwendet, um verschiedene Werte zu trennen, wenn es in einer Funktion verwendet wird

Zum Beispiel haben wir unsere endgültige Rückgabe als (zum Beispiel) intValue-Trennzeichen doubleValue-Trennzeichen ... Und dann werden wir mit dieser Zeichenfolge alle erforderlichen Informationen abrufen, die auch von verschiedenen Typen sein können

Der folgende Code zeigt die Funktionsweise dieses Konzepts

Das verwendete Trennzeichen ist ! @ # Und 3 Werte werden intVal, doubleVal und stringVal zurückgegeben

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

AUSGABE

5
3.14
hello
BUILD SUCCESSFUL (total time: 2 seconds)
Jay Dharmendra Solanki
quelle
3
yuk. Riesiger Code-Geruch. Analysieren, anstatt verfügbare objektorientierte Funktionen zu verwenden. IMO, eines der schlechtesten Beispiele für Codierung, die ich je gesehen habe. Es sei denn, Sie beschreiben eine Situation, in der Sie mehrere Werte zwischen zwei unabhängigen Programmen oder einer anderen prozessübergreifenden Kommunikation übergeben müssen und irgendwie keinen Zugriff auf einen angemessenen Mechanismus dafür haben (json oder andere).
ToolmakerSteve
-4

In C würden Sie dies tun, indem Sie Zeiger auf Platzhalter für die Ergebnisse als Argumente übergeben:

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

Versuchen wir etwas Ähnliches in 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);
Adrian Panasiuk
quelle
1
In einer OO-Sprache wird es jedoch im Allgemeinen als vorzuziehen angesehen, das zu tun, was mehrere Personen bereits vier Jahre vor dieser Antwort vorgeschlagen haben: Gruppieren Sie die beiden verwandten Werte in einem Objekt (Paar-, Tupel- oder benutzerdefinierte Klassendefinition) und erstellen Sie dann eine Liste dieser Werte Objekte. Auf diese Weise müssen nicht mehrere Listen weitergegeben werden. Dies ist besonders wichtig, wenn Sie ein solches Paar (ein Element jeder Ihrer Listen) zur weiteren Verarbeitung an andere Methoden übergeben müssen.
ToolmakerSteve
@ToolmakerSteve Zur Verdeutlichung: Die Listen sollen jeweils genau ein Element enthalten und sind nur ein Mittel, um ein Analogon zum Pass-by-Pointer zu implementieren. Sie sollen nicht mehrere Ergebnisse erfassen oder sogar weiter als ein paar Zeilen nach dem Methodenaufruf verwendet werden.
Adrian Panasiuk
-5

PASS A HASH IN DIE METHODE UND BEVÖLKERN SIE ES ......

public void buildResponse (String-Daten, Map-Antwort);

l_39217_l
quelle