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

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

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

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

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

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

Является ли список и CSV-файлы в основном разными представлениями одних и тех же данных? Похоже, что вам нужен объект, в котором у вас есть единственная ссылка на List и своего рода справочная таблица.

James P. 02.07.2011 13:29

Связанный вопрос: stackoverflow.com/questions/53453058/…

John McClane 25.11.2018 22:55
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
181
2
245 301
25

Ответы 25

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

Вы можете вернуть список объектов 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 может быть тем, что вам нужно.

Причина возврата списка, разделенного запятыми: Если я не создаю список здесь, мне придется сделать это в вызывающей программе, перебирая объекты (требуется значение CS). Возможно, я без надобности делаю предварительную оптимизацию.

Jagmal 19.01.2009 17:08

Мне всегда было интересно, почему по этой причине в Java нет класса Pair <T, U>.

David Koelle 19.01.2009 17:08

Джагмал: да, если единственный rason для возврата списка, разделенного запятыми, это оптимизация, то забудьте об этом.

Joachim Sauer 19.01.2009 17:13

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

David Hanak 19.01.2009 18:59

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

Joachim Sauer 19.01.2009 19:16

Иоахим - я сомневаюсь, что вы это увидите, поскольку сообщение такое старое, но мне интересно, не могли бы вы объяснить, почему вы считаете, что возвращать объекты нескольких типов - это «запах кода». Какие проблемы это вызывает или на какие типы плохого дизайна указывает? Спасибо.

J. Taylor 26.04.2011 19:19

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

Joachim Sauer 26.04.2011 19:23

@DavidKoelle Вы имеете в виду что-то вроде двунаправленной карты?

Weckar E. 11.09.2017 12:45

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

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

Jagmal 19.01.2009 17:04

@Jagmal: вы можете повторить цикл дважды, но в большинстве случаев это не имеет значения (см. Ответ штуковин).

Joachim Sauer 19.01.2009 17:06

Да, не пытайтесь оптимизировать свой код, если в этом нет необходимости. gizmo очень прав насчет этого.

Bombe 19.01.2009 17:11

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

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

public Map<String, MyType> doStuff();

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

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

Это не сработает, если какие-либо имена совпадают. Список может содержать дубликаты, но карта не может (содержать повторяющиеся ключи).

tvanfosson 19.01.2009 17:08

Конечно. Я делал предположения на основе вопроса - возможно, неоправданно :)

kipz 19.01.2009 17:11

Хотя в данном случае ваше предположение верно, я перехожу к преждевременной оптимизации (чего мне не следует делать).

Jagmal 19.01.2009 17:16

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

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

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

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

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

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

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

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 сделает менее раздражающим. Есть некоторая работа по созданию действительно хороших re: generics, но теперь все должно быть в порядке. (см. PECS)
  • hashcode и equals генерируются eclipse.
  • приведение кода времени компиляции в методе cast в порядке, но кажется не совсем правильным.
  • Я не уверен, нужны ли подстановочные знаки в isInstance.
  • Я только что написал это в ответ на комментарии, только для иллюстрации.

Я обычно использую этот класс в каждой кодовой базе, над которой я работаю. Я бы также добавил: реализацию hashCode / equals и, возможно, статические методы isInstance () и cast ().

jamesh 21.01.2009 16:49

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

David Hanak 21.01.2009 19:05

@jamesh: не могли бы вы написать здесь подробно свою пару? Я хотел бы знать, как это выглядит после предоставления «реализации hashCode / equals и, возможно, статических методов isInstance () и cast ()». Спасибо.

Qiang Li 13.08.2011 04:38

@QiangLi - я обычно генерирую хэш-код и методы равенства. Метод экземпляра isInstance принимает два класса и проверяет, являются ли экземпляры a и b экземплярами этих классов. Cast берет пару <?,?> И аккуратно применяет ее к паре <A, B>. Реализации должны быть довольно простыми (подсказка: Class.cast (), Class.isInstance ())

jamesh 16.08.2011 02:04

@Jamesh, я был бы очень признателен, если бы вы могли объяснить то, что вы здесь сказали. Может быть, отправили как другой ответ? Я бы, конечно, проголосовал за вас! Я просто не знаю, как сделать то, что вы сказали. Спасибо!

Qiang Li 16.08.2011 02:58

Это очень хорошая реализация Pair. Я бы сделал одно небольшое изменение: добавил сообщение в ClassCastException. В противном случае отладка станет кошмаром, если по какой-то причине это не удастся. (и предупреждения о подавлении rawtypes были бы ненужными, если бы вы выполняли приведение к Pair<?,?> (что работает, потому что вам нужны только методы Object из a и b). Вы не возражаете, если я настрою код?

Joachim Sauer 05.06.2013 16:47

IMHO, более подходящие имена для полей a и b в Pair - это first и second, поскольку это указывает, что известно об этих полях. С другой стороны, для такого базового класса некоторые люди предпочитают более короткие имена полей, поэтому a и b - неплохой выбор.

ToolmakerSteve 14.06.2015 01:54

Я почти всегда заканчиваю тем, что определяю классы 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.

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

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

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

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

извините за комментарий к вашему очень старому ответу, но как обратные вызовы относятся к сборке мусора? У меня, конечно, нет хорошего понимания управления памятью в java, если у вас есть объект A, вызывающий объект B.getResult(), а B.getResult() вызывает A.finishResult() как callback, собирает ли объект B мусор или он остается там до завершения A ?? наверное, глупый вопрос, но у меня есть фундаментальная путаница!

wired00 05.06.2012 10:07

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

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

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

Потому что это PITA - определять класс всякий раз, когда вам нужно вернуть несколько значений, просто потому, что в языке отсутствует эта обычно полезная функция;) А если серьезно, то, что вы предлагаете, очень часто стоит делать.

ToolmakerSteve 21.11.2014 00:19

ПРОЙДИТЕ ХЭШ В МЕТОД И НАСЕЛЕНИЕ ЕГО ......

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

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

}

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

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

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

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

Однако в объектно-ориентированном языке обычно считается предпочтительным сделать то, что несколько человек уже предлагали за четыре года до этого ответа: сгруппировать два связанных значения в один объект (пару, кортеж или определение настраиваемого класса), а затем получить список этих объекты. Это позволяет избежать необходимости передавать несколько списков. Это становится особенно важным, если нужно передать такую ​​пару (по одному элементу каждого из ваших списков) другим методам для дальнейшей обработки.

ToolmakerSteve 21.11.2014 00:10

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

Adrian Panasiuk 22.11.2014 06:57

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

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

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

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;

Пара имеет преимущество управления типом класса

Hlex 05.08.2014 07:39

ИМХО, не идите по этому пути - в декларации слишком мало сказано об ожидаемых возвращаемых значениях. AFAIK, более широко предпочтительнее создавать универсальные классы, которые определяют, сколько параметров возвращается, и тип этих параметров. Pair<T1, T2>, Tuple<T1, T2, T3>, Tuple<T1, T2, T3, T4> и т.д. Затем конкретное использование показывает количество и типы параметров Pair<int, String> temp = ... или что-то еще.

ToolmakerSteve 14.06.2015 02:03

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

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

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

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

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

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

ToolmakerSteve 21.11.2014 00:06

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

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

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

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

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

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

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

Случай первый.

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

Вместо:

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

В Apache Commons для этого есть кортеж и тройка:

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

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

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

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

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

return new Pair<>(yourList, yourCommaSeparatedValues);

Сделайте это простым и создайте класс для ситуаций с множественными результатами. В этом примере принимается 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;
}

}

Вы можете использовать HashMap<String, Object> следующим образом

public HashMap<String, Object> yourMethod()
{

   .... different logic here 

  HashMap<String, Object> returnHashMap = new HashMap<String, Object>();
  returnHashMap.put("objectA", objectAValue);
  returnHashMap.put("myString", myStringValue);
  returnHashMap.put("myBoolean", myBooleanValue);

  return returnHashMap;
}

Затем при вызове метода в другой области вы можете вернуть каждый объект к его исходному типу:

// call the method
HashMap<String, Object> resultMap = yourMethod();
                
// fetch the results and cast them
ObjectA objectA = (ObjectA) resultMap.get("objectA");
String myString = (String) resultMap.get("myString");
Boolean myBoolean = (Boolean) resultMap.get("myBoolean");

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

Вот мой ход:

import java.util.Objects;

public final class NTuple<V, T extends  NTuple<?, ?>> {
    private final V value;
    private final T next;

    private NTuple(V value, T next) {
        this.value = value;
        this.next = next;
    }

    public static <V> NTuple<V, ?> of(V value) {
        return new NTuple<>(value, null);
    }

    public static <V,  T extends  NTuple<?, ?>> NTuple<V, T> of(V value, T next) {
        return new NTuple<>(value, next);
    }

    public V value() {
        return value;
    }

    public T next() {
        return next;
    }

    public static <V> V unpack0(NTuple<V, ?> tuple) {
        return Objects.requireNonNull(tuple, "0").value();
    }

    public static <V, T extends NTuple<V, ?>> V unpack1(NTuple<?, T> tuple) {
        NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
        NTuple<V, ?> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
        return tuple1.value();
    }

    public static <V, T extends NTuple<?, NTuple<V, ?>>> V unpack2(NTuple<?, T> tuple) {
        NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
        NTuple<?, NTuple<V, ?>> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
        NTuple<V, ?> tuple2 = Objects.requireNonNull(tuple1.next(), "2");
        return tuple2.value();
    }
}

Пример использования:

public static void main(String[] args) {
    // pre-java 10 without lombok - use lombok's var or java 10's var if you can
    NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> multiple = wordCount("hello world");
    String original = NTuple.unpack0(multiple);
    Integer wordCount = NTuple.unpack1(multiple);
    Integer characterCount  = NTuple.unpack2(multiple);

    System.out.println(original + ": " + wordCount + " words " + characterCount + " chars");
}

private static NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> wordCount(String s) {
    int nWords = s.split(" ").length;
    int nChars = s.length();
    return NTuple.of(s, NTuple.of(nWords, NTuple.of(nChars)));
}

Плюсы:

  • не настраиваемый контейнерный класс - нет необходимости писать класс только для возвращаемого типа
  • n-длина - может обрабатывать любое количество возвращаемых значений
  • no-cast - нет необходимости приводить из Object
  • безопасный по типу - типы проверяются с помощью дженериков Java

Минусы:

  • неэффективен для большого количества возвращаемых значений
    • согласно моему опыту работы с множественными возвращаемыми значениями python, на практике этого не должно происходить
  • объявления тяжелых типов
    • можно облегчить ломбок / Java 10 var

Другие вопросы по теме