Java: Как передать byte [] по ссылке? - PullRequest
21 голосов
/ 02 декабря 2008

Вы можете сделать это в .NET, используя ключевое слово «ref». Есть ли способ сделать это в Java?

Ответы [ 3 ]

46 голосов
/ 02 декабря 2008

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

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

Другими словами, если ваш метод выглядит так:

public void doSomething(byte[] data)
{
    for (int i=0; i < data.length; i++)
    {
        data[i] = (byte) i;
    }
}

тогда ты в порядке. Если ваш метод выглядит так:

public void createArray(byte[] data, int length)
{
    // Eek! Change to parameter won't get seen by caller
    data = new byte[length]; 
    for (int i=0; i < data.length; i++)
    {
        data[i] = (byte) i;
    }
}

тогда вам нужно изменить его на:

public byte[] createArray(int length)
{
    byte[] data = new byte[length]; 
    for (int i=0; i < data.length; i++)
    {
        data[i] = (byte) i;
    }
    return data;
}

или

public class Holder<T>
{
    public T value; // Use a property in real code!
}

public void createArray(Holder<byte[]> holder, int length)
{
    holder.value = new byte[length]; 
    for (int i=0; i < length; i++)
    {
        holder.value[i] = (byte) i;
    }
}

Подробнее см. Передача параметров в C # и Передача параметров в Java . (Боюсь, первое написано лучше, чем второе. Однажды я перейду к обновлению.)

18 голосов
/ 02 декабря 2008

На самом деле в Java ссылки передаются по значению .

В этом случае ссылка является объектом byte[]. Любые изменения, которые влияют на сам объект, будут видны из метода вызывающей стороны.

Однако, если вы пытаетесь заменить ссылку, например, с помощью new byte[length], вы заменяете только ссылку, полученную путем передачи по значению, поэтому вы не меняете ссылку в методе вызывающего абонента.

Вот интересное прочтение об этой проблеме: Ява, черт возьми, передается по значению!


Вот конкретный пример:

public class PassByValue
{
    public static void modifyArray(byte[] array)
    {
        System.out.println("Method Entry:  Length: " + array.length);
        array = new byte[16];
        System.out.println("Method Exit:   Length: " + array.length);
    }

    public static void main(String[] args)
    {
        byte[] array = new byte[8];
        System.out.println("Before Method: Length: " + array.length);
        modifyArray(array);
        System.out.println("After Method:  Length: " + array.length);
    }
}

Эта программа создаст массив byte длины 8 в методе main, который вызовет метод modifyArray, в котором создается новый массив byte длины 16.

Может показаться, что при создании нового массива byte в методе modifyArray длина массива byte при возврате к методу main будет 16, однако при запуске этой программы раскрывает что-то другое:

Before Method: Length: 8
Method Entry:  Length: 8
Method Exit:   Length: 16
After Method:  Length: 8

Длина массива byte после возврата из метода modifyArray возвращается к 8 вместо 16.

Почему это?

Это потому, что метод main вызвал метод modifyArray и отправил скопированную ссылку на new byte[8] с использованием передачи по значению . Затем метод modifyArray отбросил скопированную ссылку, создав new byte[16]. К тому времени, когда мы оставляем modifyArray, ссылка на new byte[16] выходит за рамки (и в конечном итоге будет собирать мусор). Однако метод main по-прежнему имеет ссылку на new byte[8], поскольку он только отправил скопированную ссылку , а не фактическую ссылку на ссылку.

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

7 голосов
/ 02 декабря 2008

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

  • Примитивы (int, boolean и т. Д.) - это особые случаи в Java, а не объекты как таковые. В этом случае копия примитива (аргумента) передается в функцию. Это хорошо сочетается с теорией прохода по стоимости.
  • Что касается объектов, то происходит то, что ссылка на объект передается по значению (вместо ссылки делается копия ссылки) ... но обе ссылки указывают на один и тот же объект. Поэтому, если вы измените параметр объекта в методе, фактический объект будет изменен.

Эта статья должна помочь вам .. http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html

Что касается вопроса ОП, просто передайте ссылку на массив byte [] методу. Чистый результат будет аналогичен передаче по ссылке. Если вы измените байтовый массив, вызывающая сторона сможет увидеть изменения после выполнения метода.

Обновление для подавления сопротивления:) => указывает на вывод

.NET Land

class Counter
{
  private int m_count = 0;
  public override string  ToString()
  {
     return String.Format("Counter ID{0} : Value {1}", this.GetHashCode(), m_count);
  }
  public void Increment()
  {  m_count++;  }
}
class MakeAPass
{
   public void PassByValueAndModify(int i)
   {   i = 20;    }

   public void PassByRefAndModify(ref int i)
   {   i = 20;   }

   public void PassByValueAndModify(Counter c)
   {   c.Increment();   }

   public void PassByRefAndModify(ref Counter c)
   {   c.Increment();   }

   public void PassByRefAndReassign(ref Counter c)
   {
      c = new Counter();
      for (int i=0; i<5; ++i)
         c.Increment();
   }
}

static void Main(string[] args)
{
   MakeAPass obj = new MakeAPass();
   int intVal = 10;
   obj.PassByValueAndModify(intVal);
   Console.WriteLine(intVal);              // => 10
   obj.PassByRefAndModify(ref intVal);
   Console.WriteLine(intVal);              // => 20

   Counter obCounter = new Counter();
   obj.PassByValueAndModify(obCounter);
   Console.WriteLine(obCounter.ToString());  // => Counter ID58225482 : Value 1
   obj.PassByRefAndModify(ref obCounter);
   Console.WriteLine(obCounter.ToString());  // => Counter ID58225482 : Value 2
   obj.PassByRefAndReassign(ref obCounter);
   Console.WriteLine(obCounter.ToString());  // => Counter ID54267293 : Value 5
}

Ява Земля

Незначительные моды Требуется: используйте hashCode () и + для объединения строк в Counter.java ...

class MakeAPass
{
   public void PassByValueAndModify(int i)
   {   i = 20;   }

   // can't be done.. Use Integer class which wraps primitive
   //public void PassByRefAndModify(ref int i)

   public void PassByValueAndModify(Counter c)
   {   c.Increment();   }

   // same as above. no ref keyword though
   //public void PassByRefAndModify(ref Counter c)

   // this can't be done as in .net
   //public void PassByRefAndReassign(ref Counter c)
   public void PassAndReassign(Counter c)
   {
      c = new Counter();
      for (int i=0; i<5; ++i)
         c.Increment();
   }
}
public static void main(String args[])
{
   MakeAPass obj = new MakeAPass();
   int intVal = 10;
   obj.PassByValueAndModify(intVal);
   System.out.println(intVal);                 // => 10 
   //obj.PassByRefAndModify(ref intVal);
   //System.out.println(intVal);               // can't get it to say 20

   Counter obCounter = new Counter();
   obj.PassByValueAndModify(obCounter);
   System.out.println(obCounter.ToString());    // => Counter ID3541984 : Value 1
   //obj.PassByRefAndModify(ref obCounter);
   //Console.WriteLine(obCounter.ToString());   // no ref. but can make it 2 by repeating prev call
   obj.PassAndReassign(obCounter);
   System.out.println(obCounter.ToString());    // => Counter ID3541984 : Value 1
                                                // can't get it to say 5  
}
...