After some own confusion which specific meaning final declared method parameters have this blog entry will try to clarify this.

At least the final keyword on method parameters can be seen as an indicator for the Java compiler that this parameter can not be reassigned to another reference. Java parameter handling is always Call by Value (yes, even when dealing with Objects) and here comes why.: It is true, that Java handles a reference to the Object when dealing with non-primitive data types. The Object itself is not passed from the callee to the target function! Instead a reference is passed that points to the desired Object. But this reference is not equal to the one on callee side since it is just a copy. What is passed to a function is a copied reference as value - ok, everyone's still on board? :-) Maybe Java should use the more matching explanation Call by Copied Reference as Value.

To sum up:
Java exclusively passes ALL method parameters (primitive data types or references to objects) in Call by Value style!
 As a proof for this let's have a look at the following demo code and its output.
 /**
 * Call by Value Test Application.
 *
 * @author Christopher Meyer
 * @version 0.1
 * Apr 21, 2012
 */
public class CBVTest {

    public static void main(String[] args) {
        Integer mainInternInteger = new Integer(1);

        /*
         * Even references are copied during calls!
         *
         * Explanation Objects are never passed, only references to them, BUT
         * references are copied! So only reference COPIES reach the method.
         * Neither changes to the reference inside/outside the method will
         * influence the counterpart.
         *
         * Maybe it should be called "Call by Copied Reference as Value".
         */
        class RunMe implements Runnable {

            Integer runnerInternInteger;

            public RunMe(Integer i) {
                runnerInternInteger = i;

                /*
                 * The following operation will have no effect on the main
                 * thread, since the reference to "i" is a copied one.
                 * Interfacing the "caller" reference is prevented.
                 */
                i = new Integer(3);
            }

            @Override
            public void run() {
                while (true) {
                    System.out.println(runnerInternInteger.intValue()
                            + "\t (runner intern value)");
                }
            }
        }

        Thread runner = new Thread(new RunMe(mainInternInteger));
        runner.start();

        // Create a new object and assign it to "mainInternInteger".
        mainInternInteger = new Integer(2);
        while (true) {
            System.out.println(
                    mainInternInteger.intValue() + "\t (main intern value)");
        }
    }
}
The output of the code looks like this:
...
2     (main intern value)
2     (main intern value)
2     (main intern value)
2     (main intern value)
1     (runner intern value)
2     (main intern value)
1     (runner intern value)
2     (main intern value)
1     (runner intern value)
2     (main intern value)
1     (runner intern value)

1     (runner intern value)

1     (runner intern value)

1     (runner intern value)

1     (runner intern value)

...
So neither the assignment to the handled parameter (i = new Integer(3)), nor the reassignment from the calling class (mainInternInteger = new Integer(2)) have any influence on each other.

So what is it worth if it isn't really necessary? 
If added to the Constructor of RunMe (public RunMe(final Integer i)) the reassignment i = new Integer(3) throws an exception: Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - final parameter i may not be assigned. It prevents failures related to unintentional reassignment. Accidental assignments to the handled parameter will always fail! final forces a developer to produce accurate code.

The final keyword is not part of the method signature. So if declared final or not, the compiled code will be identical (everyone can easily check this by using diff). This means that a method can't be overloaded by declaring the method parameters once final and once not. Since the byte code remains identical it also has absolutely no influence on performance.

To confuse even more keep in mind that inner classes require to define a variable final when the variable can be modified (for example when dealing with anonymous inner classes for Threads - if this isn't clear to you consider multiple variables in the same context with identical names that can be altered).

Kommentare ansehen

Wird geladen...