0

In particular I'm wondering if the arg is always linked or is it just linked because the function decloration is static?

It'd be nice to know if their are any quirks before I write thousands of lines of code expecting it to be one way, but in 1 edge case it's not.

For the record I understand that string = new String[2]; inside changeValue() would create a new string and the changes would not propogate to main's instance of string;

Here's some example code to illustrate what I'm referring to.

public class TestLink {

    public static void changeValue(Object object) {
        object.foo = "changed";
    }

    public static void changeValue(String[] string) {
        string[0] = "changed";
    }

    public static void main(String[] args) {
        String[] string = new String[2];
        string[0] = "not changed";
        changeValue(string);
        if (string[0].equals("not changed")) {
            System.out.println("not linked");
        } else {
            System.out.println("linked");
        }
        System.out.println(string[0]);
        /*
        output:
        linked
        changed
        */
    }
}
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
the7erm
  • 195
  • 1
  • 9
  • 4
    What do you mean by "linked"? It's not at all clear what you're asking here. – T.J. Crowder Nov 25 '15 at 08:53
  • *"For the record I understand that string = new String[2]; inside changeValue() would create a new string..."* No, it wouldn't create any strings at all. It would create an *array*, with room for two `String` references, but no strings. *"...and the changes would not propogate to main's instance of string;"* Correct. – T.J. Crowder Nov 25 '15 at 08:54
  • duplicate of: http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value – guillaume girod-vitouchkina Nov 25 '15 at 08:55
  • I know what you mean. Read up on "pass by value", "pass by reference", and "references": all in the context of Java. `static` has nothing to do with it. A `static` function has no `this`. That's essentially the only difference. – Bathsheba Nov 25 '15 at 08:55
  • @Bathsheba: "pass by reference" and object references have nothing to do with one another; the term is not relevant here. – T.J. Crowder Nov 25 '15 at 08:58
  • We can agree to disagree on that. I consider all these topics to be related. I've observed that beginners find a reference by value a tricky concept. – Bathsheba Nov 25 '15 at 09:05
  • @T.J.Crowder "pass by reference" is exactly what this question is about. It **is** useful to understand. – mad_manny Nov 25 '15 at 09:07
  • @mad_manny: No, it isn't. "Pass by reference" is a term of art in our profession, with a *specific meaning*. That meaning is: Passing a reference to a **variable** into a function so the function can modify the contents **of the variable**. Java doesn't have any form of pass-by-reference at all. – T.J. Crowder Nov 25 '15 at 09:17
  • @Bathsheba: Well, yes, we disagree. I think bringing up a completely unrelated concept is actively unhelpful when trying to understand passing object references around. It makes people like mad_manny thing there's something special about passing an object reference vs. a primitive, which there simply isn't: It's a *value* being passed either way. – T.J. Crowder Nov 25 '15 at 09:22
  • 1
    @T.J. Crowder: Got it. You're definitely right. – mad_manny Nov 25 '15 at 09:26

2 Answers2

4

From the code in the question, it sounds like what you're really asking is: "Is there an edge case where I pass an array reference into a method and modify its contents, but that modification doesn't actually happen?"

No, there isn't. Let's extract some of that code and change some names so they aren't misleading:

public static void changeValue(String[] arg) {
    arg[0] = "changed";
}

public static void main(String[] args) {
    String[] foo = new String[2];
    foo[0] = "not changed";
    changeValue(foo);
    if (foo[0].equals("not changed")) {
        System.out.println("not linked");
    } else {
        System.out.println("linked");
    }
    System.out.println(foo[0]);
}

changeValue accepts an array reference (a value that points to an array), and changes the contents of that array. If the code compiles (e.g., all the types and such are correct) and doesn't throw when run (you're not passing null or a zero-length array into changeValue), then no, there's no edge case where that won't work.

What's going on in that code?

If we look deeply at what's going on in that code, we can see why the answer is "no." I find pictures can help, so let's throw some ASCII-art at this.

This line:

String[] foo = new String[2];

gives us this in memory:

                  +----------+
foo(ref 5426)---->| String[] |
                  +----------+
                  | 0: null  |
                  | 1: null  |
                  +----------+

We have a variable, foo, that contains a value. The value is called an object reference because it refers to an object (in this case, an array, so we sometimes call it an array reference). That's just a value that tells the JVM where the object is. I'm showing it as ref 5426 just to show that it's a value like any other value, not something magical; I've picked 5426 completely at random, we never see the actual value, the JVM hides it from us.

Then you do this:

    foo[0] = "not changed";

Giving us:

                  +-------------+
foo(ref 5426)---->| String[]    |
                  +-------------+     +---------------+
                  | 0: ref 1897 |---->|    String     |
                  | 1: null     |     +---------------+
                  +-------------+     | "not changed" |
                                      +---------------+

E.g., the first entry in the array contains value which is a reference to a string object.

Then we do this:

    changeValue(foo);

That takes the value from foo and passes that value to changeValue. changeValue receives the value in the argument arg. So now we have this:

                  +-------------+
foo(ref 5426)--+->| String[]    |
               |  +-------------+     +---------------+
               |  | 0: ref 1897 |---->|    String     |
               |  | 1: null     |     +---------------+
               |  +-------------+     | "not changed" |
               |                      +---------------+
arg(ref 5426)--+

Since main hasn't returned, foo still exists, and it has the value ref 5426. That value was copied into arg, so arg also has the value ref 5426. That value tells the JVM where the array is.

Then you do:

    arg[0] = "changed";

which gives us:

                  +-------------+                       +---------------+
foo(ref 5426)--+->| String[]    |                       |    String     |
               |  +-------------+     +-----------+     +---------------+
               |  | 0: ref 2785 |---->|  String   |     | "not changed" |
               |  | 1: null     |     +-----------+     +---------------+
               |  +-------------+     | "changed" |
               |                      +-----------+
arg(ref 5426)--+

The first entry in the array no longer points to the old string, you've replaced the value with a new value that points to a new string. (The old string hangs around until the garbage collector gets around to getting rid of it, but we no longer care, we don't have any references to it.)

changeValue is done, so it returns, and arg (the argument) goes away.

Since foo still has the same value (ref 5426), when you look up the first entry in it, you see the value that was put there by changeValue.

How does that tell us the answer is "no"?

For there to be an edge case where the above didn't work, there'd have to be one of these things going on:

  • Passing foo's value to into changeValue and receiving it as arg not working for some reason.

  • Passing foo's value to into changeValue and receiving it as arg making a copy of the array for some reason.

  • There being some difference where we got ref 5426 from that matters when we use it to get the array and change it.

There aren't any edge cases where any of those things happen. :-)

T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • Please note this wont work for primitive types (int, float, boolean, byte, ...), as primitives will be passed by value to the method. It will still work for arrays of privitive types as arrays are not primitive. – mad_manny Nov 25 '15 at 09:05
  • @mad_manny: It's not that it "won't work" for primitives, it's *irrelevant* for primitives. Primitives don't refer to something else in memory that has state, only object references (including array references) do. Doing `array = new String[2]` would do exactly what assigning to an `int` or other primitive-typed argument would do (which is, have no effect at all on the variable in the calling function). You're conflating unrelated concepts. – T.J. Crowder Nov 25 '15 at 09:19
  • 1
    Wow. This is a *very* good answer. The OP will do well to study it carefully, as did I. – Bathsheba Nov 25 '15 at 09:59
  • Thank you very much. This definitely answers my question. – the7erm Nov 26 '15 at 12:18
  • @the7erm: Good deal, I'm glad that helped. If this answered the question, the way SO works, you'd [accept](/help/someone-answers) the answer by clicking the checkmark next to it. – T.J. Crowder Nov 26 '15 at 12:24
0

Function declared as static means exactly nothing in this context. What You did is that You passed an String array. This array, inside function, was the same array as in main function. So an array is passed by reference * and String type is a special case, that passing it creates new String, despite being non-primitive type. Primitive types are passed by value only.

Your edge cases are listed here, plus mentioned String. byte, short, int, long, float, double, boolean, char - all of these are, as You call them, "not linked". These are not passed by reference *.

*Java doesn't pass by reference per-se. It is the references that are passed by value. Is Java "pass-by-reference" or "pass-by-value"?

EDIT

Forget about pass by reference in Java. This language is purely based on pass by value concept (unlinked, as You call it), but references are passed by values themselves, thus creating Your linked behaviour. Credit from this edit goes to T.J. Crowder. I merely used term pass by reference for my own convenience.

Community
  • 1
  • 1
DreamOnJava
  • 585
  • 4
  • 12
  • No, it is **not** pass-by-reference. It's pass-by-value, as you flagged up later. The value being passed is an array reference. It's *never* useful to conflate passing references by value with pass-by-reference, which is an unrelated concept that Java doesn't have. Just don't use the term at all in the answer, it only increases the confusion on this people like mad_manny have. – T.J. Crowder Nov 25 '15 at 09:20
  • For me it makes things clear. Java passes references by value and thus, behaves similarly to real pass by reference. – DreamOnJava Nov 25 '15 at 09:26