2009-09-10 4 views
3

С сторонним API я заметил следующее.Выходные параметры в Java

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

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

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

public static void getString(String output){ 

} 

, и я получаю "продукция" строка, назначенная.

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

+6

Этот пример неправильный. Java не имеет выходных параметров. – Bombe

+0

@ Bombe: http://www.javapractices.com/topic/TopicAction.do?Id=37 –

+3

@Chathuranga: более подробно рассмотрим пример. Он использует StringBuilder, а не строку! –

ответ

21

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

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" будет выводиться, не "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" 
} 
1

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

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

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

+0

почему вообще используется StringBuilder? –

+0

Вы спрашиваете «что такое использование StringBuilder» или «Зачем использовать StringBuilder в этом случае»? Обычно StringBuilder предназначен для быстрого построения строк и конкатенации (как следует из названия). В этом случае это единственный способ добиться желаемого эффекта (возвращение значения строки путем изменения аргумента функции). Вы не можете сделать это с помощью объекта String, поскольку он является неизменным. –

3

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

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

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

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

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

0

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

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

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

+0

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

+0

@Bombe: (1) Даже если создание объекта дешево, создание объекта _and_ присваивание не может быть дешевле, чем просто присвоение (2) Это не только создание, которое стоит; есть также сборщик мусора, который стоит. (3) Есть Java-среды, где создание объектов чрезвычайно дорого (Java-карта). Здесь, по возможности, следует избегать создания объекта. – Curd

1

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

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

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

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

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

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

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

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

+1

На мой взгляд, это очень уродливый и плохой способ вернуть несколько результатов. Вместо этого создайте класс (bean), который содержит несколько вещей, которые вам нужно вернуть, и пусть метод возвращает экземпляр этого. Чуть хуже: Верните объект [], содержащий несколько вещей. – Jesper

4

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

bool IDictionary.TryGet(key, out value); 

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

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

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

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

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

1

На самом деле, невозможно получить параметры в java, но вы можете сделать работу вокруг того, чтобы метод принял де-ссылку для неизменяемых String и примитивов, написав общий класс, где неизменяемым является общий с значение и 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 ставит: Remainding элементов равно 2

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