Возможно, эта теория отсутствует в моем образовании по программированию, но можно ли в объектно-ориентированных языках создать метод, который может return использовать две или более переменных вместо одной.

Например. Мне очень нравится, как Matlab справляется с этим: [var1, var2] = myFunction(input);

Если, например, мне нужно только var1, я могу:

[var1, ~] = myFunction(input);

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

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

Я подумал о следующем подходе к этому, но мне было интересно, есть ли способ лучше.

public static Double myFunction(Double input, Integer method)
{
    double calcOne   = input*5;
    double calcTwo   = input*25;
    double calcThree = input*300;

    switch(method)
    {
      case 1:  return calcOne;

      case 2:  return calcTwo;

      case 3:  return calcThree;

      default: return calcOne;
    } 
}

Обратите внимание, что мне явно нужно разделение между операторами return и вычислениями (вычисления в реальной программе включают в себя большой for loop и чтение из файлов и т. Д.)

Редактировать

Причина, по которой я не использую 3 разных метода, заключается в том, что я использую в своем методе очень большой for loop. Моему основному методу иногда требуется одна переменная return из этого метода, а иногда и две.

Поэтому (лучшим) решением может быть следующее:

public static ArrayList<Double> myFunction(double input, int[] method)
{
    double calcOne   = input*5;
    double calcTwo   = input*25;

    ArrayList<Double> returnValues = new ArrayList<Double>();

    if (Arrays.asList(method).contains(1))
    {
        returnValues.add(calcOne);
    }
    if (Arrays.asList(method).contains(2))
    {
        returnValues.add(calcTwo);
    }

    return returnValues;
}

Где int[] method содержит имена переменных, которые должна возвращать функция.

0
Jean-Paul 12 Мар 2014 в 03:22
3
Если вы хотите вернуть более одной переменной, почему бы вам не вернуть объект с этими переменными или массив?
 – 
morgano
12 Мар 2014 в 03:25
@morgano: потому что мне не нужны ВСЕ возвращаемые переменные каждый раз, когда я вызываю функцию (я вызываю ее несколько раз). Возврат объекта также означает сохранение объекта в основном потоке, включая сохранение всех (ненужных) временных переменных. Не лучше ли решить эту проблему внутри метода?
 – 
Jean-Paul
12 Мар 2014 в 03:26
@morgano: учитывая тот факт, что мои фактические возвращаемые переменные имеют огромные размеры, помещение их в массив, а затем извлечение только соответствующей записи кажется излишним.
 – 
Jean-Paul
12 Мар 2014 в 03:33
1
Возвращаемое значение всегда является одним ... "объектом". Однако этот объект может быть ЛЮБЫМ. Вы можете вернуть одно значение, например return 1, или вы можете вернуть список, класс, список списков карт строк и классов или все, что вам нужно.
 – 
dognose
12 Мар 2014 в 03:34
2
Этот вопрос не имеет ничего общего с тем, является ли язык объектно-ориентированным, частично объектно-ориентированным или нет. Такой синтаксис можно было бы добавить в Java, не делая его менее объектно-ориентированным. Было бы сложно определить, как это будет работать, и все правила для него, тем более что это компилируемый язык со строгой типизацией. Это не имеет ничего общего с ООП.
 – 
ajb
12 Мар 2014 в 03:41

6 ответов

Лучший ответ

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

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

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

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

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

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

Используемый разделитель - ! @ # и Возвращаются 3 значения intVal, doubleVal и 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);
            }
        }

ВЫХОД

5
3.14
hello
BUILD SUCCESSFUL (total time: 2 seconds)
2
Jay Dharmendra Solanki 20 Мар 2014 в 14:29
1
Я должен сказать, что это довольно гениально! Очень креативное решение!
 – 
Jean-Paul
20 Мар 2014 в 14:57
1
Недостатком, однако, является то, что мне все еще нужно использовать разные типы данных. Но это остается очень креативным решением!
 – 
Jean-Paul
20 Мар 2014 в 14:58
Я в основном использую подход с разделителем и в других местах... В основном для передачи информации с одного конца на другой с ограничением использования только строк
 – 
Jay Dharmendra Solanki
20 Мар 2014 в 15:01

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

public ClassA {
      public String var1;
      public int var2;
      public char var3;
      public Long var4;
}

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

2
MeIr 12 Мар 2014 в 03:28
Но вызов объекта означает, что все переменные сохраняются. В моей реализации выше только соответствующая переменная сохраняется (навсегда), а остальные отбрасываются.
 – 
Jean-Paul
12 Мар 2014 в 03:30
Нет проблем, сохраняйте только те, которые вам нужны. Все остальное либо не сохраняется в объекте, либо делает их равными нулю, если они вам не нужны. Сборщик мусора освободит память для вас.
 – 
MeIr
12 Мар 2014 в 03:32
@Melr: Но как я могу хранить разные переменные, если функция остается прежней?
 – 
Jean-Paul
12 Мар 2014 в 03:37
Конечно можно, почему бы и нет? Хотите привести пример?
 – 
MeIr
12 Мар 2014 в 03:51
@Melr: я не понимаю. Функция заполняет переменные (var1, var2, var3, var4). Если функция остается той же самой, как два вызова функции могут привести к разным популяциям?
 – 
Jean-Paul
12 Мар 2014 в 03:57

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

  1. Использовать массив

    Object [] myarray = new Object [length];
    

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

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

Если все ваши возвращаемые значения имеют один и тот же тип данных, тогда намного проще создать массив этого единственного типа данных и избежать этого ограничения:

  String [] mystrings = new String [length];
  1. Используйте List

Этот параметр работает так же, как массив, но списки обладают некоторой дополнительной гибкостью. Список создается так:

  List<Object> mylist = new LinkedList<Object>();

Элементы добавляются так:

  mylist.add(myVar);

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

  1. Создать объект

Вы можете объявить класс с несколькими параметрами. Эти параметры имеют определенные типы данных, поэтому они могут использовать все встроенные библиотеки Java для своего типа данных без приведения типа или преобразования. Это позволяет избежать основного недостатка Array или List Объектов.

    public ReturnObject () {
         public int param1;
         public String myString;
    }

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

2
ColeS 12 Мар 2014 в 04:01

Да, это возможно. Вроде, как бы, что-то вроде.

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

В других OO (и не OO) языках есть более прямая поддержка; например на некоторых языках (Python, Ruby, Perl и т. д.) можно сделать что-то вроде этого:

  [a, b, c] = someMethodReturningATuple()

Синтаксис LHS присваивает значения переменным a, b и c.

Java не поддерживает это, но отчасти потому, что у него нет встроенного кортежного типа или типа «любой массив». А без одного из них было бы трудно впихнуть это в язык. (И гипотетический тип «массив чего угодно», вероятно, не подойдет, потому что он будет полагаться на динамическую типизацию на стороне вызывающего.)


Обратите внимание, что это не имеет ничего общего с OO по сравнению с не-OO ... или даже статически типизированными по сравнению с динамически типизированными языками. (Впервые я использовал множественный возврат в языке Mesa в 1980-х. Mesa был статически типизирован.) Это больше связано с набором лингвистических функций, которые, по мнению разработчиков соответствующего языка, необходимо поддерживать.

2
Stephen C 12 Мар 2014 в 05:44
Но все же это (с использованием объекта массива) заставляет меня хранить переменные вне метода. Я бы предпочел иметь дело с операторами возврата внутри метода, чтобы убедиться, что избыточные переменные являются временными и отброшены.
 – 
Jean-Paul
12 Мар 2014 в 03:39
@ Жан-Поль - переменные не отбрасываются. Ценности делают.
 – 
Stephen C
12 Мар 2014 в 03:45

E.g. I really like Matlab's way of handling this: [var1, var2] = myFunction(input);

Пожалуйста, никогда не считайте "Matlab" языком программирования: P

Эквивалент того, что вы ищете, будет примерно таким:

List<Double> result = doSomething(5);
System.out.println(result.get(0)); // 25

public List<Double> doStomething(Integer input){
   List<Double> result= new LinkedList<Double>();

   Double calcOne = input*5;
   Double calcTwo = input*25;

   result.add(calcOne);
   result.add(calcTwo);
   return result;
}

Однако вычисляются ВСЕ значения, даже если вы просто хотите использовать «Первый результат». Если вам нужен только ОДИН из результатов - создайте 3 метода и вызовите то, что вам нужно, например:

public Double calcOne(Integer input){
   ...
}

public Double calcTwo(Integer input){
   ...
}

public Double calcThree(Integer input){
   ...
}
1
dognose 12 Мар 2014 в 03:40
1
Считаете ли вы Perl языком программирования? :) :)
 – 
ajb
12 Мар 2014 в 03:43
Ха-ха, хорошо, тогда я буду использовать numerical computing environment. Решение с несколькими методами тривиально, но не мой вопрос.
 – 
Jean-Paul
12 Мар 2014 в 03:44
@Jean-Paul Вы также можете использовать один метод с разными возвращаемыми значениями c. Но предполагая, что ваши вычисления всегда зависят от ввода и не так тривиальны, как x*y - почему бы не использовать отдельные методы?
 – 
dognose
12 Мар 2014 в 03:47
1
Не отвечая на это :) Не могу справиться с отрицательными отзывами: P
 – 
dognose
12 Мар 2014 в 03:50
1
@ Жан-Поль, я думаю, что нет «неправильного» или «правильного» решения. Это зависит от ваших потребностей и личных предпочтений. Если вам нужно, чтобы ваш метод мог обрабатывать от 1 до n возвращаемых значений - ОДИН параметризованный метод - это путь. (Никто не хочет писать 785 методов, возвращающих до 785 результатов). Если это больше похоже на рейтинг, где вам часто нужно занимать 1-3 места, вам также пригодятся такие методы, как getFirst(); getSecond(); getThird(). «В основном» вы можете написать целое приложение с помощью ОДНОГО метода — или вы можете разделить каждый «символ», напечатанный отдельным методом — это зависит от вас! :)
 – 
dognose
12 Мар 2014 в 04:07

Другая идея - создать класс, который использует тип «хэш-карта» для хранения значений параметров. Этот класс предоставляет методы, например, для добавления параметра и значения, для поиска значения для параметра, удаления параметра, для получения количества параметров и т. Д. Важно скрыть использование типа данных для хранения Информация. В случае, если вы хотите изменить тип "hashmap" чем-то другим, клиент вашего класса не будет затронут, ПОКА вы не измените публичный интерфейс своего класса.

< sizesПример

public class Context {

private HashMap<String, Object> attributes = new HashMap<String, Object>();

// Get value for parameter name
public Object getAttribute(String name) {
    return attributes.get(name);
}

// Set value for parameter name
public void setAttributes(String name, Object value) {
    removeAttributes(name);
    attributes.put(name, value);
}

// Returns the number of values 
public int getSize() {
    return this.attributes.size();
}

// remove parameter
public void removeAttributes(String name) {
    Iterator<Map.Entry<String, Object>> iter = this.attributes.entrySet().iterator();
    while (iter.hasNext()) {
        Map.Entry<String, Object> entry = iter.next();
        if (name.equalsIgnoreCase(entry.getKey())) {
            iter.remove();
            }
        }
    }
} 

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

@Test
public void testContext() {

    // Le context
    Context context = new Context();

    Integer param1 = new Integer(1);
    BigDecimal param2 = new BigDecimal(19700101);
    String param3 = new String("hello world");

    // Add 3 parameters (key, value)
    context.setAttributes("param_1", param1);
    context.setAttributes("param_2", param2);
    context.setAttributes("param_3", param3);
    Assert.assertEquals(3, context.getSize()); // test the number of key-value mappings in this map.

    Integer resultParam1 = (Integer) context.getAttribute("param_1");
    BigDecimal resultParam2 = (BigDecimal) context.getAttribute("param_2");
    String resultParam3 = (String) context.getAttribute("param_3");

    Assert.assertEquals(param1, resultParam1);
    Assert.assertEquals(param2, resultParam2);  
    Assert.assertEquals(param3, resultParam3);  

    // remove parameter
    context.removeAttributes("param_1");
    Assert.assertEquals(2, context.getSize());  // 3add - 1remove = 2 parameters

    // Replace value parameter by another value
    Long value = new Long(1976);
    context.setAttributes("param_2", value);

    // and so on ...
}
1
ben 12 Мар 2014 в 08:37