Как вернуть несколько объектов из метода Java?

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

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

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

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

Как бы то ни было, возврат HashMap выглядит не очень изящным способом.

Ответ 1

Если вы хотите вернуть два объекта, вы обычно хотите вернуть один объект, который инкапсулирует два объекта.

Вы можете вернуть список объектов NamedObject, например:

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

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

Затем вы можете легко вернуть List<NamedObject<WhateverTypeYouWant>>.

Также: Почему вы хотите вернуть список имен, разделенных запятыми, вместо List<String>? Или еще лучше, верните Map<String,TheObjectType>, когда ключи являются именами и значениями объектов (если только ваши объекты не указали порядок, и в этом случае NavigableMap может быть тем, что вы хотите.

Ответ 2

Если вы знаете, что собираетесь вернуть два объекта, вы также можете использовать общую пару:

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

Изменить Более полная реализация вышеперечисленного:

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

    }

}

Примечания, в основном вокруг ржавости с Java и дженериками:

  • оба a и b неизменяемы. Статический метод
  • makePair помогает вам при наборе плиток котла, который алмазный оператор в Java 7 будет делать менее раздражающим. Там какая-то работа, чтобы сделать это действительно приятным: generics, но теперь это должно быть нормально. (c.f. PECS)
  • hashcode и equals генерируются eclipse.
  • время выполнения компиляции в методе cast в порядке, но не кажется правильным.
  • Я не уверен, нужны ли символы isInstance.
  • Я только что написал это в ответ на комментарии только для иллюстрации.

Ответ 3

Если метод, который вы вызываете, является приватным или вызывается из одного места, попробуйте

return new Object[]{value1, value2};

Вызывающий выглядит так:

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

Пример пары с Дэвидом Ханаком не имеет синтаксического преимущества и ограничен двумя значениями.

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

И вызывающий объект выглядит так:

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

Ответ 4

Я почти всегда заканчиваю определение классов n-Tuple при кодировании на Java. Например:

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

Я знаю это немного уродливо, но он работает, и вам просто нужно определить типы кортежей один раз. Кортежи - это то, чего действительно не хватает Java.

EDIT: пример Дэвида Ханака более изящный, поскольку он избегает определения геттеров и по-прежнему сохраняет объект неизменным.

Ответ 5

Мы должны забыть о небольшой эффективности, скажем, около 97% времени: преждевременная оптимизация - корень всего зла.

Д. Кнут

Ответ 6

До Java 5 я бы согласился с тем, что решение Map не является идеальным. Это не даст вам проверку типа времени компиляции, поэтому может возникнуть проблемы во время выполнения. Однако с Java 5 у нас есть общие типы.

Итак, ваш метод может выглядеть так:

public Map<String, MyType> doStuff();

MyType, конечно, являющийся типом возвращаемого объекта.

В принципе, я думаю, что возвращение карты является правильным решением в этом случае, потому что именно то, что вы хотите вернуть - сопоставление строки с объектом.

Ответ 7

Вы можете использовать любой из следующих способов:

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) Использование Массив

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) Использование 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) Использование 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) Используя собственный класс контейнера

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) Использование 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) Используя Пара 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());
}

Ответ 8

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

С вашей конкретной проблемой это будет выглядеть примерно так:

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

Ответ 9

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

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

(для примитивных типов данных я использую незначительные вариации для непосредственного сохранения значения)

Затем метод, который хочет вернуть несколько значений, будет объявлен следующим образом:

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

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

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

Преимущества (по сравнению с другими предлагаемыми решениями):

  • Вам не нужно создавать специальное объявление класса для отдельных методов и его возвращаемых типов
  • Параметры получают имя и поэтому легче различаются при просмотре сигнатуры метода
  • Тип безопасности для каждого параметра

Ответ 10

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

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

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

Ответ 11

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

Ответ 12

Apache Commons имеет кортеж и тройку для этого:

  • ImmutablePair<L,R> Неизменяемая пара, состоящая из двух объектов элементы.
  • ImmutableTriple<L,M,R> Неизменяемая тройка, состоящая из три элемента объекта.
  • MutablePair<L,R> Измененная пара, состоящая из два элемента объекта.
  • MutableTriple<L,M,R> Изменчивая тройка состоящий из трех элементов объекта.
  • Pair<L,R> Пара, состоящая из два элемента.
  • Triple<L,M,R> Тройка, состоящая из трех элементов.

Источник: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html

Ответ 13

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

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

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

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

Ответ 14

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

Ответ 15

Я следовал аналогичному подходу, чем описанный в других ответах, с несколькими настройками на основе требования, которое у меня было, в основном я создал следующие классы (на всякий случай, все это 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!");
    }
}

Ответ 16

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

return new Pair<>(yourList, yourCommaSeparatedValues);

Ответ 17

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

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

Ответ 18

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

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

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

Ответ 19

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

}

Ответ 20

Я использую очень простой подход для решения проблем множественных возвратов. Он служит цели и позволяет избежать сложности.

Я называю это разделителем строк

И он эффективен, поскольку он может даже возвращать значения Несколько типов, например. int, double, char, строка и т.д.

В этом подходе мы используем строку, которая вряд ли встречается в целом. Мы называем это разделителем. Этот разделитель будет использоваться для разделения различных значений при использовании в функции

Например, у нас будет наш окончательный возврат как (например) разделитель intValue separator doubleValue... И затем, используя эту строку, мы будем извлекать всю необходимую информацию, которая также может быть разных типов

Следующий код покажет работу этой концепции

Используемый разделитель ! @# и  3 значения возвращаются intVal, doubleVal и stringVal

        public class TestMultipleReturns {

            public static String multipleVals() {

                String result = "";
                String separator = "[email protected]#";


                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("[email protected]#")[0]);
                // Code to process intVal

                double doubleVal = Double.parseDouble(res.split("[email protected]#")[1]);
                // Code to process doubleVal

                String stringVal = res.split("[email protected]#")[2];

                System.out.println(intVal+"\n"+doubleVal+"\n"+stringVal);
            }
        }

ВЫХОД

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

Ответ 21

Передайте список вашему методу и заполните его, затем верните строку с именами, например:

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

Затем назовите его следующим образом:

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

Ответ 22

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

Случай один.

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

Вместо:

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

Try:

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

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


Случай два.

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

Вместо:

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

Try:

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

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

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

Ответ 23

Держите его простым и создайте класс для множественной ситуации с результатами. Этот пример принимает ArrayList и текст сообщения из базы данных 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;
}

}

Ответ 24

В C вы должны сделать это, передав указатели на заполнители для результатов в качестве аргументов:

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

Попробуйте что-то подобное в 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);

Ответ 25

ПРОЙДИТЕ ВСТУПЛЕНИЕ В МЕТОД И ПОПУЛЯТЬ ЭТО......

public void buildResponse (данные строки, ответ карты);