Jak zwrócić wiele obiektów z metody Java?


172

Chcę zwrócić dwa obiekty z metody Java i zastanawiałem się, jaki może być dobry sposób na zrobienie tego?

Możliwe sposoby mogę myśleć to: powrót HashMap(od dwóch obiektów są podobne) lub zwracać ArrayListz Objectobiektów.

Mówiąc dokładniej, dwa obiekty, które chcę zwrócić, to (a) Listobiekty i (b) ich nazwy oddzielone przecinkami.

Chcę zwrócić te dwa obiekty z jednej metody, ponieważ nie chcę iterować po liście obiektów, aby uzyskać nazwy oddzielone przecinkami (co mogę zrobić w tej samej pętli w tej metodzie).

W jakiś sposób zwrócenie HashMapnie wygląda na zbyt elegancki sposób.


1
Czy lista i pliki CSV są zasadniczo różnymi poglądami na te same dane? Wygląda na to, że potrzebujesz obiektu, w którym masz jedno Listodniesienie i rodzaj tabeli przeglądowej.
James P.,

Odpowiedzi:


128

Jeśli chcesz zwrócić dwa obiekty, zwykle chcesz zwrócić pojedynczy obiekt, który zamiast tego hermetyzuje te dwa obiekty.

Możesz zwrócić listę NamedObjectobiektów w następujący sposób:

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

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

Następnie możesz łatwo zwrócić plik List<NamedObject<WhateverTypeYouWant>>.

Ponadto: dlaczego chcesz zwrócić listę nazwisk rozdzielonych przecinkami zamiast znaku List<String>? Albo jeszcze lepiej, zwróć a Map<String,TheObjectType>z kluczami będącymi nazwami i wartościami obiektów (chyba że twoje obiekty mają określoną kolejność, w takim przypadku a NavigableMapmoże być tym, czego chcesz.


Powód zwracania listy rozdzielanej przecinkami jest następujący: Jeśli nie utworzę listy tutaj, musiałbym to zrobić w programie wywołującym, przechodząc przez obiekty (potrzebna jest wartość CS). Być może niepotrzebnie optymalizuję wstępnie.
Jagmal,

2
Zawsze się zastanawiałem, dlaczego Java nie ma klasy Pair <T, U> z tego powodu.
David Koelle,

Jagmal: tak, jeśli jedynym powodem zwracania listy oddzielonej przecinkami jest ta optymalizacja, to zapomnij o tym.
Joachim Sauer

Działa to dobrze tylko wtedy, gdy elementy, które chcesz zwrócić, są tej samej klasy lub przynajmniej mają bliskiego wspólnego przodka. Chodzi mi o to, że użycie Object zamiast WhatersTypeYouWant nie jest zbyt schludne.
David Hanak,

@David: Zgadzam się, że używanie Object tutaj nie jest zbyt fajne, ale z drugiej strony zwracanie obiektów bez wspólnego przodka (z wyjątkiem oczywiście Object) również nie jest zbyt fajne. Powiedziałbym nawet, że to zapach kodu, jeśli tego potrzebujesz.
Joachim Sauer

69

Jeśli wiesz, że zwrócisz dwa obiekty, możesz również użyć pary ogólnej:

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

Edytuj Pełniejsza implementacja powyższego:

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

    }

}

Uwagi, głównie na temat rustykalności w Javie i generycznych:

  • obie ai bsą niezmienne.
  • makePairMetoda statyczna pomaga w pisaniu na tabliczkach kotłowych, które operator diamentu w Javie 7 uczyni mniej irytującym. Jest trochę pracy, aby zrobić to naprawdę fajne re: generics, ale teraz powinno być ok. (por. PECS)
  • hashcodei equalssą generowane przez zaćmienie.
  • rzutowanie czasu kompilacji w castmetodzie jest w porządku, ale nie wydaje się całkiem poprawne.
  • Nie jestem pewien, czy symbole wieloznaczne isInstancesą konieczne.
  • Napisałem to w odpowiedzi na komentarze, wyłącznie w celach ilustracyjnych.

Zwykle ta klasa puka w każdej bazie kodu, nad którą pracuję. Dodałbym również: implementację hashCode / equals i prawdopodobnie statyczną metodę isInstance () i cast ().
jamesh

Jasne, istnieje wiele sposobów, aby uczynić tę klasę inteligentniejszą i wygodniejszą w użyciu. Powyższa wersja zawiera to, co wystarczy w jednorazowej deklaracji.
David Hanak

@jamesh: czy możesz napisać tutaj pełną parę szczegółów? Chciałbym wiedzieć, jak to wygląda po dostarczeniu „implementacji hashCode / equals i prawdopodobnie statycznej metody isInstance () i cast ()”. Dziękuję Ci.
Qiang Li

@QiangLi - Zwykle generuję metody hashcode i equals. Metoda instancji isInstance przyjmuje dwie klasy i zapewnia, że ​​a & b instancji są instancjami tych klas. Cast bierze parę <?,?> I ostrożnie rzuca ją do pary <A, B>. Implementacje powinny być dość łatwe (wskazówka: Class.cast (), Class.isInstance ())
jamesh

2
To bardzo fajna Pairrealizacja. Jedna drobna zmiana, którą wprowadziłbym: dodaj wiadomość do pliku ClassCastException. W przeciwnym razie debugowanie stanie się koszmarem, jeśli z jakiegoś powodu to się nie powiedzie. (a rawtypes tłumić-ostrzeżenia byłoby zbędne, jeśli chcesz przesyłać Pair<?,?>(który pracuje, bo trzeba tylko Objectmetody od aa b) Czy Mogę dostosować kod.?
Joachim Sauer

25

W przypadku, gdy wywoływana metoda jest prywatna lub wywoływana z jednej lokalizacji, spróbuj

return new Object[]{value1, value2};

Osoba dzwoniąca wygląda następująco:

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

Przykład pary autorstwa Davida Hanaka nie ma korzyści składniowych i jest ograniczony do dwóch wartości.

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

A dzwoniący wygląda następująco:

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

7
Para ma korzyści z kontroli typu klasy
Hlex

5
IMHO, nie idź tą drogą - deklaracja za mało mówi o oczekiwanych wartościach zwracanych. AFAIK, szerzej preferowane jest tworzenie klas ogólnych, które określają liczbę zwracanych parametrów i typ tych parametrów. Pair<T1, T2>, Tuple<T1, T2, T3>, Tuple<T1, T2, T3, T4>, Itd. Wtedy stosowanie specyficznych pokazuje liczbę i typy parametrów Pair<int, String> temp = ...czy cokolwiek innego.
ToolmakerSteve

22

Możesz użyć dowolnego z następujących sposobów:

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) Korzystanie z Array

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) Korzystanie z 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) Korzystanie z 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) Korzystanie z niestandardowej klasy kontenera

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) Korzystanie z 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) Korzystanie z Pair of 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());
}

16

Prawie zawsze kończę definiowanie klas n-Tuple, kiedy koduję w Javie. Na przykład:

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

Wiem, że jest trochę brzydki, ale działa i wystarczy, że raz zdefiniujesz typy krotek. Krotki to coś, czego w Javie naprawdę brakuje.

EDYCJA: przykład Davida Hanaka jest bardziej elegancki, ponieważ unika definiowania getterów i nadal utrzymuje niezmienność obiektu.


9

Przed Java 5 zgadzałbym się, że rozwiązanie Map nie jest idealne. Nie daje możliwości sprawdzenia typu podczas kompilacji, więc może powodować problemy w czasie wykonywania. Jednak w przypadku języka Java 5 mamy typy ogólne.

Twoja metoda mogłaby więc wyglądać następująco:

public Map<String, MyType> doStuff();

MyType jest oczywiście typem zwracanego obiektu.

Zasadniczo uważam, że zwrócenie mapy jest właściwym rozwiązaniem w tym przypadku, ponieważ jest to dokładnie to, co chcesz zwrócić - mapowanie ciągu znaków na obiekt.


To nie zadziała, jeśli którekolwiek z nazw kolidują. Lista może zawierać duplikaty, ale mapa nie może (zawierać zduplikowanych kluczy).
tvanfosson

Oczywiście. Robiłem założenia na podstawie pytania - być może niesłusznie :)
kipz

Chociaż twoje założenie jest prawdziwe w tym przypadku, wchodzę w dziedzinę przedwczesnej optymalizacji (której nie powinienem robić).
Jagmal,

6

Alternatywnie, w sytuacjach, w których chcę zwrócić kilka rzeczy z metody, czasami używam mechanizmu wywołania zwrotnego zamiast kontenera. Działa to bardzo dobrze w sytuacjach, w których nie mogę z góry określić, ile obiektów zostanie wygenerowanych.

W przypadku Twojego konkretnego problemu wyglądałoby to mniej więcej tak:

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

przepraszam za skomentowanie bardzo starej odpowiedzi, ale jak przebiegają oddzwaniania dotyczące zbierania śmieci? Z pewnością nie mam dobrego zrozumienia zarządzania pamięcią w java, jeśli masz obiekt Awywołania obiektu B.getResult()i B.getResult()wywołuje A.finishResult()jako a callback, czy obiekt Bzbiera śmieci, czy pozostaje do zakończenia A? prawdopodobnie głupie pytanie, ale mam fundamentalne zamieszanie!
przewodowe 00

6

Apache Commons ma do tego krotkę i potrójną:

  • ImmutablePair<L,R> Niezmienna para składająca się z dwóch elementów Object.
  • ImmutableTriple<L,M,R> Niezmienna trójka składająca się z trzech elementów Object.
  • MutablePair<L,R> Zmienna para składająca się z dwóch elementów Object.
  • MutableTriple<L,M,R> Zmienna trójka składająca się z trzech elementów Object.
  • Pair<L,R> Para składająca się z dwóch elementów.
  • Triple<L,M,R> Trójka składająca się z trzech elementów.

Źródło: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html


6

Chociaż w twoim przypadku komentarz może być dobrym rozwiązaniem, w Androidzie możesz użyć Pair . Po prostu

return new Pair<>(yourList, yourCommaSeparatedValues);

5

Zastosowanie następującego obiektu wpisu Przykład:

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

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

5

Jeśli chodzi o problem z wieloma wartościami zwracanymi, zwykle używam małej klasy pomocniczej, która opakowuje pojedynczą wartość zwracaną i jest przekazywana jako parametr do metody:

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

(dla prymitywnych typów danych używam niewielkich zmian, aby bezpośrednio przechowywać wartość)

Metoda, która chce zwrócić wiele wartości, zostanie wówczas zadeklarowana w następujący sposób:

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

Może główną wadą jest to, że wywołujący musi wcześniej przygotować zwracane obiekty na wypadek, gdyby chciał ich użyć (a metoda powinna sprawdzać wskaźniki zerowe)

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

Zalety (w porównaniu do innych proponowanych rozwiązań):

  • Nie musisz tworzyć specjalnej deklaracji klasy dla poszczególnych metod i zwracanych przez nie typów
  • Parametry otrzymują nazwę i dlatego łatwiej je rozróżnić, patrząc na sygnaturę metody
  • Wpisz bezpieczeństwo dla każdego parametru

Dziękujemy za rozwiązanie, które zapewnia bezpieczeństwo nazw i typów każdej zwracanej wartości, bez konieczności deklarowania klasy dla zestawu zwracanych typów wartości.
ToolmakerSteve

3

Wszystkie możliwe rozwiązania będą kludge (takie jak obiekty kontenerów, pomysł HashMap, „wielokrotne wartości zwracane” realizowane za pomocą tablic). Zalecam ponowne wygenerowanie listy oddzielonej przecinkami ze zwróconej listy. Kod będzie dużo czystszy.


Zgadzam się z Tobą, ale jeśli to zrobię, skończę na dwukrotnym zapętleniu (właściwie tworzę elementy listy jeden po drugim w istniejącej metodzie).
Jagmal,

1
@Jagmal: możesz zapętlić się dwukrotnie, ale w większości przypadków nie ma to znaczenia (zobacz odpowiedź gadżetów).
Joachim Sauer

1
Tak, nie próbuj optymalizować kodu, chyba że naprawdę musisz. gizmo ma co do tego rację.
Bombe,

3

Zachowaj prostotę i utwórz klasę dla sytuacji z wieloma wynikami. Ten przykład akceptuje ArrayList i tekst komunikatu z databasehelper getInfo.

Gdzie wywołujesz procedurę, która zwraca wiele kodowanych wartości:

multResult res = mydb.getInfo(); 

W procedurze getInfo kodujesz:

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

i zdefiniuj klasę multResult za pomocą:

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

}


2

Jak widzę, są tu naprawdę trzy możliwości, a rozwiązanie zależy od kontekstu. Możesz zdecydować się na zaimplementowanie konstrukcji nazwy w metodzie, która tworzy listę. To jest wybór, który wybrałeś, ale nie sądzę, że jest najlepszy. Tworzysz sprzężenie w metodzie producenta z metodą konsumpcji, która nie musi istnieć. Inni dzwoniący mogą nie potrzebować dodatkowych informacji, a Ty będziesz obliczać dodatkowe informacje dla tych rozmówców.

Alternatywnie możesz użyć metody wywołującej do obliczenia nazwy. Jeśli tylko jeden dzwoniący potrzebuje tych informacji, możesz się tam zatrzymać. Nie masz żadnych dodatkowych zależności i chociaż wiąże się to z dodatkowymi obliczeniami, uniknąłeś zbyt szczegółowego tworzenia metody konstrukcji. To dobry kompromis.

Wreszcie, sama lista może być odpowiedzialna za utworzenie nazwy. To jest trasa, którą wybrałbym, gdyby obliczenia musiało być wykonane przez więcej niż jednego dzwoniącego. Myślę, że to nakłada odpowiedzialność za tworzenie nazw z klasą, która jest najbliżej spokrewniona z samymi obiektami.

W drugim przypadku moim rozwiązaniem byłoby utworzenie wyspecjalizowanej klasy List, która zwraca rozdzielony przecinkami ciąg nazw obiektów, które zawiera. Spraw, aby klasa była na tyle inteligentna, aby tworzyła ciąg z nazwą w locie, gdy obiekty są dodawane i usuwane z niego. Następnie zwróć wystąpienie tej listy i w razie potrzeby wywołaj metodę generowania nazw. Chociaż prawie tak samo efektywne (i prostsze) może być po prostu opóźnienie obliczania nazw do pierwszego wywołania metody i zapisanie jej wtedy (ładowanie z opóźnieniem). Jeśli dodasz / usuniesz obiekt, wystarczy tylko usunąć obliczoną wartość i przeliczyć ją przy następnym wywołaniu.


2

Potrafi coś takiego jak krotka w języku dynamicznym (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]);
}
}

i używaj w ten sposób

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


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

2

Zastosowałem podobne podejście niż opisane w innych odpowiedziach z kilkoma poprawkami opartymi na wymaganiu, które miałem, w zasadzie stworzyłem następujące klasy (na wszelki wypadek wszystko jest w Javie):

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

Wtedy moje wymaganie było proste, w klasie repozytorium, która dociera do bazy danych, dla metody Get Methods niż pobieranie danych z DB, muszę sprawdzić, czy to się nie powiodło, czy się powiedzie, a następnie, jeśli się powiedzie, musiałem pobawić się listą zwrotną , jeśli się nie powiodło, zatrzymaj wykonywanie i powiadom o błędzie.

Na przykład moje metody są takie:

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
}

Gdzie ResultMessage to po prostu klasa z dwoma polami (kod / komunikat), a Customer to dowolna klasa z zestawem pól pochodzącym z bazy danych.

Następnie, aby sprawdzić wynik, po prostu robię to:

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

1

W C ++ (STL) istnieje para klas do łączenia dwóch obiektów. W Java Generics klasa pary nie jest dostępna, chociaż jest na nią popyt . Możesz jednak łatwo wdrożyć to samodzielnie.

Zgadzam się jednak z niektórymi innymi odpowiedziami, że jeśli chcesz zwrócić dwa lub więcej obiektów z metody, lepiej byłoby zamknąć je w klasie.


1

Dlaczego nie utworzyć WhateverFunctionResultobiektu, który zawiera wyniki i logikę wymaganą do przeanalizowania tych wyników, iteracji itd. Wydaje mi się, że albo:

  1. Te obiekty wyników są ściśle ze sobą powiązane / powiązane i należą do siebie lub:
  2. nie są ze sobą powiązane, w takim przypadku twoja funkcja nie jest dobrze zdefiniowana pod względem tego, co próbuje zrobić (tj. robi dwie różne rzeczy)

Widzę, że ten rodzaj problemu pojawia się raz po raz. Nie bój się tworzyć własnych klas kontenerów / wyników, które zawierają dane i powiązaną z nimi funkcjonalność. Jeśli po prostu przekażesz rzeczy w HashMappodobny sposób, Twoi klienci będą musieli rozebrać tę mapę na części i za każdym razem, gdy chcą skorzystać z wyników, uporządkować zawartość.


Ponieważ to PITA, aby zdefiniować klasę, gdy trzeba zwrócić wiele wartości, tylko dlatego, że język nie ma tej powszechnie użytecznej funkcji;) Ale poważnie, to, co sugerujesz, jest bardzo często warte zrobienia.
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]);
    }

}

1

To nie jest dokładna odpowiedź na pytanie, ale ponieważ każde z podanych tutaj rozwiązań ma pewne wady, proponuję spróbować nieco zmienić kod, aby zwrócić tylko jedną wartość.

Przypadek pierwszy.

Potrzebujesz czegoś zarówno wewnątrz, jak i na zewnątrz swojej metody. Dlaczego nie obliczyć go na zewnątrz i przekazać do metody?

Zamiast:

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

Próbować:

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

Powinno to zaspokoić większość twoich potrzeb, ponieważ w większości sytuacji jedna wartość jest tworzona przed drugą i możesz podzielić ich tworzenie na dwie metody. Wadą jest to, że metoda createThingsBma dodatkowy parametr w porównaniu do createThingsi prawdopodobnie przekazujesz dokładnie tę samą listę parametrów dwa razy do różnych metod.


Przypadek drugi.

Najbardziej oczywiste rozwiązanie w historii i uproszczona wersja przypadku pierwszego. Nie zawsze jest to możliwe, ale może obie wartości można stworzyć niezależnie od siebie?

Zamiast:

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

Próbować:

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

Aby uczynić go bardziej użytecznym, te dwie metody mogą mieć wspólną logikę:

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

0

Przekaż listę do metody i wypełnij ją, a następnie zwróć ciąg z nazwami, na przykład:

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

Następnie nazwij to tak:

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

-2

Stosuję bardzo podstawowe podejście do rozwiązywania problemów związanych z wielokrotnymi zwrotami. Służy celowi i unika złożoności.

Nazywam to separatorem ciągów Approach

Jest skuteczny, ponieważ może nawet zwracać wartości wielu typów, np. Int, double, char, string itp

W tym podejściu wykorzystujemy ciąg, którego wystąpienie jest bardzo mało prawdopodobne. Nazywamy to separatorem. Ten separator byłby używany do oddzielania różnych wartości, gdy jest używany w funkcji

Na przykład otrzymamy nasz końcowy zwrot jako (na przykład) intValue separator doubleValue separator ... Następnie używając tego ciągu pobierzemy wszystkie wymagane informacje, które mogą być również różnych typów

Poniższy kod pokaże działanie tej koncepcji

Użyty separator to ! @ # I zwracane są 3 wartości intVal, doubleVal i 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);
            }
        }

WYNIK

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

3
fuj. Potężny zapach kodu. Parsowanie zamiast używania dostępnych funkcji zorientowanych obiektowo. IMO, jeden z najgorszych przykładów kodowania, jakie kiedykolwiek widziałem. Chyba że opisujesz sytuację, w której musisz przekazać wiele wartości między dwoma niezależnymi programami lub inną komunikację między procesami i w jakiś sposób nie masz dostępu do przyzwoitego mechanizmu (json lub inny).
ToolmakerSteve

-4

W C zrobiłbyś to, przekazując wskaźniki do symboli zastępczych dla wyników jako argumenty:

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

Spróbujmy czegoś podobnego w Javie.

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

1
Jednak w języku obiektowym ogólnie uważa się, że lepiej jest zrobić to, co kilka osób zasugerowało już cztery lata przed tą odpowiedzią: zgrupuj dwie powiązane wartości w jeden obiekt (para, krotka lub definicja klasy niestandardowej), a następnie przygotuj ich listę obiekty. W ten sposób unika się konieczności przekazywania wielu list. Staje się to szczególnie ważne, jeśli trzeba przekazać taką parę (po jednym elemencie każdej z Twoich list) innym metodom w celu dalszego przetwarzania.
ToolmakerSteve

@ToolmakerSteve Aby wyjaśnić: listy mają zawierać dokładnie jeden element i są jedynie środkiem do implementacji analogu do wskaźnika przejścia. Nie są przeznaczone do zbierania kilku wyników ani nawet do użycia dalej niż kilka wierszy po wywołaniu metody.
Adrian Panasiuk

-5

PODAJ HASH DO METODY I ZALUDUJ JĄ ......

public void buildResponse (dane ciągów znaków, odpowiedź mapy);

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.