Выходные параметры в Java - PullRequest
4 голосов
/ 10 сентября 2009

При использовании стороннего API я заметил следующее.

Вместо использования

public static string getString(){
   return "Hello World";
}

он использует что-то вроде

public static void getString(String output){

}

и мне присваивается строка «output».

Мне любопытно узнать причину реализации такой функциональности. Каковы преимущества использования таких выходных параметров?

Ответы [ 8 ]

21 голосов
/ 10 сентября 2009

Что-то не так в вашем примере.

class Foo {

    public static void main(String[] args) {
        String x = "foo";
        getString(x);
        System.out.println(x);
    }

    public static void getString(String output){
        output = "Hello World"
    }
}

В приведенной выше программе будет выведена строка "foo", not"Hello World".

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

class Holder<T> {
    public Holder(T value) {
        this.value = value;
    }
    public T value;
}

Тогда вы можете вместо этого обойти держатель:

public static void main(String[] args) {
    String x = "foo";
    Holder<String> h = new Holder(x);
    getString(h);
    System.out.println(h.value);
}

public static void getString(Holder<String> output){
    output.value = "Hello World"
}
4 голосов
/ 29 декабря 2010

Я не согласен с Джаспером: «На мой взгляд, это действительно уродливый и плохой способ вернуть более одного результата». В .NET есть интересная конструкция, которая использует выходные параметры:

bool IDictionary.TryGet(key, out value);

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

object obj;
if (myList.TryGet(theKey, out obj))
{
  ... work with the obj;
}

Я постоянно ругаю своих разработчиков, если вижу код в старом стиле, например:

if (myList.Contains(theKey))
{
  obj = myList.Get(theKey);
}

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

3 голосов
/ 10 сентября 2009

Этот пример неверен, у Java нет выходных параметров.

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

public void doSomething(String[] output) {
    output[0] = "Hello World!";
}

Но ИМХО это отстой на нескольких уровнях. :)

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

1 голос
/ 08 августа 2012

На самом деле, невозможно иметь параметры в java, но вы можете обойти метод, заставляющий метод принимать разыменование для неизменяемой строки и примитивов, либо написав универсальный класс, где неизменяемый является универсальным с value и setter и getter или с помощью массива, где элемент 0 (длина 1) является значением при условии, что он создается первым, потому что существуют ситуации, когда вам нужно возвращать более одного значения, когда нужно написать класс, чтобы вернуть их, где класс используется только там, это просто пустая трата текста и не может использоваться повторно.

Теперь, будучи C / C ++, а также .Net (моно или MS), я убежден, что java не поддерживает хотя бы разыменование примитивов; поэтому вместо этого я прибегаю к массиву.

Вот пример. Допустим, вам нужно создать функцию (метод), чтобы проверить, является ли индекс действительным в массиве, но вы также хотите вернуть оставшуюся длину после проверки индекса. Давайте назовем его в c как 'bool validate_index (int index, int arr_len, int & rem)'. Способ сделать это в Java был бы «Boolean validate_index (int index, int arr_len, int [] rem1)». rem1 означает, что массив содержит 1 элемент.

public static Boolean validate_index(int index, int arr_len, int[] rem1)
{
    if (index < 0 || arr_len <= 0) return false;

    Boolean retVal = (index >= 0 && index < arr_len);

    if (retVal && rem1 != null) rem1[0] = (arr_len - (index + 1));

    return retVal;

}

Теперь, если мы используем это, мы можем получить и логическое возвращение, и остаток.

 public static void main(String[] args)
 {
    int[] ints = int[]{1, 2, 3, 4, 5, 6};
    int[] aRem = int[]{-1};
    //because we can only scapegoat the de-ref we need to instantiate it first.
    Boolean result = validate_index(3, ints.length, aRem);

    System.out.println("Validation = " + result.toString());
    System.out.println("Remainding elements equals " + aRem[0].toString());

 }

ставит: Validation = True ставит: остальные элементы равны 2

Элементы массива всегда указывают либо на объект в стеке, либо на адрес объекта в куче. Таким образом, использование его в качестве разыменования абсолютно возможно даже для массивов, сделав его двойным массивом, создав его экземпляр как myArrayPointer = new Class [1] [], а затем передав его, потому что иногда вы не знаете, какая длина массива будет до вызова, проходящего через алгоритм типа Boolean tryToGetArray (SomeObject o, T [] [] ppArray), который будет таким же, как в c / c ++, как 'template bool tryToGetArray (SomeObject * p, T ** ppArray)' или C # 'bool tryToGetArray (SomeObject o, ref T [] array)'. Он работает и работает хорошо, пока экземпляр [] [] или [] создается в памяти первым хотя бы с одним элементом.

1 голос
/ 10 сентября 2009

Строка неизменна, вы не можете использовать псевдо-выходные параметры Java с неизменяемыми объектами.

Кроме того, область действия output ограничена методом getString . Если вы измените переменную output , вызывающая сторона ничего не увидит.

Однако вы можете изменить состояние параметра. Рассмотрим следующий пример:

void handle(Request r) {
    doStuff(r.getContent());
    r.changeState("foobar");
    r.setHandled();
}

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

Преимущества:

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

Эта функциональность имеет один большой недостаток - она ​​не работает. Параметры функции являются локальными для функции, и их присвоение не оказывает влияния вне функции.
С другой стороны

void getString(StringBuilder builder) {
    builder.delete(0, builder.length());
    builder.append("hello world");
}

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

0 голосов
/ 10 сентября 2009

, на мой взгляд, это полезно, когда у вас есть несколько результатов в функции.

0 голосов
/ 10 сентября 2009

Иногда этот механизм может избежать создания нового объекта.

Пример: Если соответствующий объект все равно существует, быстрее передать его методу и изменить какое-то поле.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...