views:

3845

answers:

3

You can do it in .NET by using the keyword "ref". Is there any way to do so in Java?

Thanks in advance!

+6  A: 

Java uses pass by value for method arguments.

  • Primitives (int, boolean, etc.) are special cases in Java.. not objects per se. In this case, a copy of the primitive (argument) is passed into the function. This gels well with the pass by value theory.
  • For Objects, what happens is that the ref to the object is passed by value (a copy of the reference is made rather than the object)... but both references point to the same object. So if you modify an object parameter in a method, the actual object will be modified.

This article should help you out.. http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html

As for the OP's question, just pass in the reference to the byte[] array to the method. The net result would be similar to pass by reference. If you modify the byte array, the caller will be able to see the changes post method execution.

Update to quell the resistance :) => indicates output

.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
}

Java Land

Minor mods reqd: Use hashCode() and + to concat strings in 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  
}
Gishu
I don't see primitives as "special cases" in terms of parameter passing. Whether the value of an expression is a primitive value or a reference, it is passed by value. The value of the parameter inside the method is the same as the value of the argument, but the variables aren't aliased.
Jon Skeet
There's a *very* significant difference between pass by value and pass by reference here - if the method changes the value of the parameter to something else (e.g. someParameter = new byte[200]) then the caller won't see that. Unfortunately we don't know whether Mahendra needs that or not.
Jon Skeet
hold on.. let me work up a response.. gimme a few minutes.
Gishu
Oscar: I think Gishu knows exactly what he's talking about, actually (although a few details are inaccurate in the answer). The value of a variable (including a parameter) is never an object, so he wasn't claiming that objects were being copied.
Jon Skeet
Not just a .NET background - a computer science background. Pass-by-reference semantics are pretty well defined, and Java doesn't have them.
Jon Skeet
there you go. phew... has been a while since i played javac and java on the command line.
Gishu
awm.. That is some "strange" java code!! :) Thanks for the post Gishu. It is clearer now. Hey, I've learned some "other" new thing today: "quell". I've never read it before
OscarRyz
Just looked up the dict online.. I used it in the 'pacify'/'allay' sense of the word and not 'suppress' :) I like throwing words around..
Gishu
+11  A: 

Actually, in Java, the references are passed-by-value.

In this case, the reference is a byte[] object. Any changes that affect the object itself will be seen from the caller method.

However, if you try to replace the reference, for example using new byte[length], you are only replacing the reference that you obtained by pass-by-value, so you are not changing the reference in the caller method.

Here's an interesting read about this issue: Java is Pass-by-Value Dammit!


Here's an concrete example:

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);
    }
}

This program will create a byte array of length 8 in the main method, which will call the modifyArray method, where the a new byte array of length 16 is created.

It may appear that by creating a new byte array in the modifyArray method, that the length of the byte array upon returning to the main method will be 16, however, running this program reveals something different:

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

The length of the byte array upon returning from the modifyArray method reverts to 8 instead of 16.

Why is that?

That's because the main method called the modifyArray method and sent a copied reference to the new byte[8] by using pass-by-value. Then, the modifyArray method threw away the copied reference by creating a new byte[16]. By the time we leave modifyArray, the reference to the new byte[16] is out of scope (and eventually will be garbage collected.) However, the main method still has reference to the new byte[8] as it only sent the copied reference and not an actual reference to the reference.

That should demonstrate that Java will pass reference using pass-by-value.

coobird
+11  A: 

What are you doing in your method? If you're merely populating an existing array, then you don't need pass-by-reference semantics - either in .NET or in Java. In both cases, the reference will be passed by value - so changes to the object will be visible by the caller. That's like telling someone the address of your house and asking them to deliver something to it - no problem.

If you really want pass-by-reference semantics, i.e. the caller will see any changes made to the parameter itself, e.g. setting it to null or a reference to a different byte array, then either method needs to return the new value, or you need to pass a reference to some sort of "holder" which contains a reference to the byte array, and which can have the (possibly changed) reference grabbed from it later.

In other words, if your method looks likes this:

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

then you're fine. If your method looks like this:

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;
    }
}

then you need to change it to either:

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

or:

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;
    }
}

For more details, read Parameter passing in C# and Parameter passing in Java. (The former is better written than the latter, I'm afraid. One day I'll get round to doing an update.)

Jon Skeet
I think you might want to remove the "Eek!" comment from the last example. Other than that, excellent answer. I was writing a similar one, but yours is much more detailed.
Hosam Aly
Ooh, thanks. Missed that in a rush to get off to work.
Jon Skeet
@Oscar's edit: Rolled back to "brace at start of line" - there's enough diversity of brace placement in the Java community (unlike, say, method naming) that I really don't think we need to start trying to enforce uniformity on SO. Doing so will just start holy wars.
Jon Skeet
@Jon - Java's one of the few languages (and the only C-style one I know of) which has a fairly widespread coding convention - so much so that it's referred to (inaccurately) as "java style". Why fight the convention? Although an edit like that is asking for trouble, why not rise above it?
Draemon
+1 for the really nice answer though.
Draemon
@Draemon: When it comes to naming (other than private variables), I'm completely with you - Sun's conventions are almost universally applied. However for bracing, there's no clear dominant style. Google is the first place I've worked which actually uses Sun's style. Open source projects vary widely.
Jon Skeet
Jon, you must have worked in some stange places. The Java style is clear. We certainly use it here! ;)
Tom Hawtin - tackline
Perhaps in places where the background was not java :( I'm deleting all my other comments ( because they don't add anything to the thread ) Here's a ref of Sun Coding conventions. http://java.sun.com/docs/codeconv/html/CodeConventions.doc6.html#430 You can take a look at java.lang package too. :)
OscarRyz
@Oscar: Yes, I know about the coding conventions. However, you should accept that they're not nearly as uniformly accepted as the naming conventions - and are far less important as they're not visible outside the source code. And no, the places where I've used "brace at start" *were* Java places.
Jon Skeet