Как вернуть несколько объектов из метода Java? - PullRequest
160 голосов
/ 19 января 2009

Я хочу вернуть два объекта из метода Java, и мне было интересно, что может быть хорошим способом сделать это?

Возможные способы, которые я могу придумать: вернуть HashMap (поскольку два объекта связаны) или вернуть ArrayList из Object объектов.

Если быть более точным, два объекта, которые я хочу вернуть, это (a) List объектов и (b) разделенные запятыми имена одного и того же.

Я хочу вернуть эти два объекта из одного метода, потому что я не хочу перебирать список объектов, чтобы получить имена через запятую (что я могу сделать в том же цикле в этом методе).

Почему-то возвращение HashMap не выглядит очень элегантным способом сделать это.

Ответы [ 25 ]

4 голосов
/ 29 марта 2013

Использование следующего объекта Entry Пример:

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

return new AbstractMap.simpleEntry<A,B>(instanceOfA,instanceOfB);
}
4 голосов
/ 01 июня 2017

Хотя в вашем случае комментарий может быть хорошим вариантом, в Android вы можете использовать Pair . Просто

return new Pair<>(yourList, yourCommaSeparatedValues);
3 голосов
/ 19 января 2009

Все возможные решения будут клуджами (например, объекты-контейнеры, ваша идея HashMap, «множественные возвращаемые значения», реализованные через массивы). Я рекомендую восстановить разделенный запятыми список из возвращенного списка. Код в конечном итоге станет намного чище.

2 голосов
/ 22 сентября 2017

Сохраняйте это простым и создайте класс для ситуации с несколькими результатами. В этом примере принимается ArrayList и текст сообщения из базы данныхhelper getInfo.

Где вы вызываете подпрограмму, которая возвращает несколько значений, которые вы кодируете:

multResult res = mydb.getInfo(); 

В процедуре getInfo вы вводите код:

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

и определите класс multResult с помощью:

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 голосов
/ 19 января 2009

На мой взгляд, здесь действительно три варианта, и решение зависит от контекста. Вы можете выбрать реализацию конструкции имени в методе, который создает список. Это выбор, который вы выбрали, но я не думаю, что он лучший. Вы создаете связь в методе источника с методом потребления, который не должен существовать. Другим абонентам может не потребоваться дополнительная информация, и вы будете рассчитывать дополнительную информацию для этих абонентов.

В качестве альтернативы вы можете заставить вызывающий метод вычислить имя. Если есть только один абонент, которому нужна эта информация, вы можете остановиться на этом. У вас нет дополнительных зависимостей, и, хотя требуется немного дополнительных вычислений, вы избежали слишком специфического метода построения. Это хороший компромисс.

Наконец, вы могли бы самим отвечать за создание имени. Это тот маршрут, по которому я бы пошел, если расчеты должны выполняться несколькими звонящими. Я думаю, что это возлагает ответственность за создание имен с классом, который наиболее тесно связан с самими объектами.

В последнем случае моим решением было бы создание специализированного класса List, который возвращает строку с разделителями-запятыми имен объектов, которые он содержит. Сделайте класс достаточно умным, чтобы он создавал строку имени на лету, когда объекты добавляются и удаляются из него. Затем верните экземпляр этого списка и при необходимости вызовите метод генерации имени. Хотя может быть почти так же эффективно (и проще) просто отложить вычисление имен до первого вызова метода и затем сохранить его (отложенная загрузка). Если вы добавляете / удаляете объект, вам нужно только удалить вычисленное значение и пересчитать его при следующем вызове.

2 голосов
/ 28 апреля 2015

Я следовал аналогичному подходу, описанному в других ответах, с некоторыми изменениями, основанными на моих требованиях, в основном я создал следующие классы (на всякий случай, все на Java):

public class Pair<L, R> {
    final L left;
    final R right;

    public Pair(L left, R right) {
        this.left = left;
        this.right = right;
    }

    public <T> T get(Class<T> param) {
        return (T) (param == this.left.getClass() ? this.left : this.right);
    }

    public static <L, R> Pair<L, R> of(L left, R right) {
        return new Pair<L, R>(left, right);
    }
}

Тогда мое требование было простым: в классе репозитория, который достигает БД, для методов Get, чем извлечение данных из БД мне нужно проверить, не удалось или не получилось, тогда, если получится, мне нужно было поиграть с возвращающий список, в случае неудачи, останавливает выполнение и сообщает об ошибке.

Так, например, мои методы такие:

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
}

Где ResultMessage - это просто класс с двумя полями (код / ​​сообщение), а Customer - это любой класс с группой полей, поступающих из БД.

Затем, чтобы проверить результат, я просто делаю это:

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!");
    }
}
2 голосов
/ 21 мая 2013

Может делать что-то вроде кортежа в динамическом языке (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]);
}
}

и используйте вот так

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


Tuple t = returnMultiValues();
ArrayList list = t.at(0,ArrayList.class);
1 голос
/ 19 января 2009

В C ++ (STL) есть парный класс для объединения двух объектов. В Java Generics парный класс недоступен, хотя для него существует некоторый спрос . Вы можете легко реализовать это самостоятельно.

Я согласен, однако, с некоторыми другими ответами, что если вам нужно вернуть два или более объектов из метода, было бы лучше инкапсулировать их в классе.

1 голос
/ 08 октября 2015

Это не совсем ответ на вопрос, но, поскольку каждое из приведенных здесь решений имеет свои недостатки, я предлагаю немного реорганизовать ваш код, поэтому вам нужно вернуть только одно значение.

Дело одно.

Вам нужно что-то как внутри, так и снаружи вашего метода. Почему бы не рассчитать это снаружи и передать его методу?

Вместо:

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

Попробуйте:

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

Это должно охватывать большинство ваших потребностей, поскольку в большинстве ситуаций одно значение создается раньше другого, и вы можете разделить его, создав два метода. Недостатком является то, что метод createThingsB имеет дополнительный параметр по сравнению с createThings, и, возможно, вы передаете точно один и тот же список параметров дважды различным методам.


Случай два.

Наиболее очевидное решение и упрощенная версия первого случая. Это не всегда возможно, но, может быть, оба значения могут быть созданы независимо друг от друга?

Вместо:

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

Попробуйте:

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

Чтобы сделать его более полезным, эти два метода могут иметь общую логику:

public ThingA createThingA(...) {
    doCommonThings(); // common logic
    // create thing A
}
public ThingB createThingB(...) {
    doCommonThings(); // common logic
    // create thing B
}
1 голос
/ 19 января 2009

Почему бы не создать объект WhateverFunctionResult, содержащий ваши результаты, и логику, необходимую для анализа этих результатов, повторения и т. Д. Мне кажется, что либо:

  1. Эти объекты результатов тесно связаны между собой / связаны и принадлежат друг другу, или:
  2. они не связаны, в этом случае ваша функция недостаточно четко определена с точки зрения того, что она пытается сделать (то есть делает две разные вещи)

Я вижу, что проблема такого рода возникает снова и снова. Не бойтесь создавать свои собственные классы контейнеров / результатов, которые содержат данные и связанные с ними функциональные возможности, чтобы справиться с этим. Если вы просто передаете материал в формате HashMap или подобном, тогда ваши клиенты должны разбирать эту карту и извлекать содержимое каждый раз, когда хотят использовать результаты.

...