Когда вам нужно передать по ссылке в Java, чтобы присвоить значения нескольким параметрам, как вы это делаете? - PullRequest
3 голосов
/ 04 мая 2010

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

Какая лучшая альтернатива здесь, кроме редизайна программы, чего нельзя сделать?

edit: некоторые из них МОГУТ быть переработаны, и это то, что я делаю, но, например, в один момент он находит минимальные и вторые минимальные значения в огромной коллекции, а затем манипулирует ими. Я хотел разделить это на 2 метода - один находит, другой манипулирует - но кажется маловероятным, что я смогу сделать это чисто.

Ответы [ 8 ]

4 голосов
/ 04 мая 2010

Вы говорите о "возврате" нескольких значений с помощью так называемых параметров, как вы иногда находите в C, которые передаются по ссылке?

Вы попросили альтернативу возвращению класса с одним значением, но это действительно правильно.

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

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

1 голос
/ 04 мая 2010

Вы могли бы сделать что-то вроде создания универсального класса, который мог бы обрабатывать значения из функции.

private static class OutValue<T> {
    public T value;

    static <X> OutValue<X> makeOutValue(X value) {
        OutValue<X> outValue = new OutValue<X>();
        outValue.value = value;
        return outValue;
    }
}

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

void getInteger(OutValue<Integer> x)
{
    x.value = 1;
}

OutValue<Integer> outValue = OutValue.makeOutValue(0);
getInteger(outValue);
System.out.println("value = " + outValue.value);

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

1 голос
/ 04 мая 2010

Мне нужно пройти по ссылке

Чтобы быть строго точным, Java не имеет передачи по ссылке. В Java есть только один механизм передачи, и это передача по значению.

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

Это может звучать как придира, но это важно.

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

Звучит не очень объектно-ориентированным для меня, особенно после прочтения вашей правки.

0 голосов
/ 04 мая 2010

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

Следуя примеру в вопросе («найти минимальные и вторые минимальные значения в огромной коллекции и затем манипулировать ими»):

public class MinimumHolder {
    private int min;
    private int secondMin;
    public MinimumHolder(int min, int secondMin) {
        this.min = min;
        this.secondMin = secondMin;
    }

    //getters...
}

public MinimumHolder findTwoMinimums(Set<Integer> numbers) {
    // ...
    return new MinimumHolder(...);
}

public MinimumHolder manipulateData(MinimumHolder mins) {
    // do stuff with the data, this method could also be
    // declared as void if MinimumHolder was mutable
}

Вы также можете довольно легко изменить рефакторинг MinimumHolder, чтобы он мог содержать переменный список "минимумов", которые были выставлены с помощью getMinimum(int position), если вы когда-либо хотели найти более двух минимумов.

0 голосов
/ 04 мая 2010

Без подробностей сложно дать конкретный совет. Но возможности включают в себя:

Поместите связанные функции в один класс и сделайте переменные-члены данных. Хотелось бы взять свой «найди и умножь два наименьших»:

class ManipulateLeast2
{
  List<int> list;
  int firstSmallest;
  int secondSmallest;

  public ManipulateLeast2(List<int> list)
  {
    this.list=list;
  }
  public void findTwoSmallest()
  {
     .. scan list and populate firstSmallest and secondSmallest ...
  }
  public void processTwoSmallest()
  {
     ... process firstSmallest and secondSmallest and do whatever ...
  }
}

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

public class Smallest
{
  int first;
  int second;
  public Smallest(int first, int second)
  {
    this.first=first;
    this.second=second;
  }
}
...
public Smallest findSmallest(List list)
{
  ... find two smallest and put in first and second ...
  // Create the object and return it
  return new Smallest(first, second);
}

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

public StringWrapper
{
  public String s;
}
...
// callee
void getTwoStrings(StringWrapper sw1, StringWrapper sw2)
{
  sw1.s="Hello";
  sw2.s="Goodbye";
} 
// caller
StringWrapper sw1=new StringWrapper();
StringWrapper sw2=new StringWrapper();
getTwoStrings(sw1, sw2);
System.out.println("First is "+sw1.s);
System.out.println("Second is "+sw2.s);

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

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

0 голосов
/ 04 мая 2010

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

import java.awt.Point;

public class Example {
    public static void main(String args[]) {
        Point pnt1 = new Point(0, 0);
        Point pnt2 = new Point(0, 0);
        System.out.println("X: " + pnt1.x + " Y: " + pnt1.y);
        System.out.println("X: " + pnt2.x + " Y: " + pnt2.y);
        System.out.println(" ");
        sillyMethod(pnt1, pnt2);
        System.out.println("X: " + pnt1.x + " Y: " + pnt1.y);
        System.out.println("X: " + pnt2.x + " Y: " + pnt2.y);
    }

    public static void sillyMethod(Point arg1, Point arg2) {
        arg1.x = 100;
        arg1.y = 100;
        arg2.x = 200;
        arg2.y = 200;
    }
}

Таким образом, вы изменяете значения объектов, не возвращая расходящиеся типы.

0 голосов
/ 04 мая 2010

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

0 голосов
/ 04 мая 2010

куча раздутых, казалось бы, ненужных классов.

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

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

или, если у вас всегда было конечное число возвращаемых элементов, вы могли бы использовать что-то вроде Pair<T1,T2> (или определить некоторый такой общий тип кортежа)

...