Final Keyword In Java

367
2
Jump to solution
03-15-2023 04:53 AM
hectorsalamanca
New Contributor III

I was wondering if it fits to include the final keyword in a method signature when giving an instance of java.lang.Number (for example, java.lang.Long)?

java.lang.Number demonstration

 

public class Demo {

    public static void main(String[] args) {
        Long longValue = 1L;
        System.out.println("Long before: " + longValue);
        System.out.println(trickyMethod(longValue));
        System.out.println("Long after: " + longValue);

        BigInteger bigIntegerValue = BigInteger.ONE;
        System.out.println("BigInteger before: "+ bigIntegerValue);
        System.out.println(trickyMethod(bigIntegerValue));
        System.out.println("BigInteger after: " + bigIntegerValue);
    }

    private static String trickyMethod(Long value) {
        value = 10L;
        System.out.println("  trickyMethod: " + value);

        if (value.equals(10L))
            return "  equal";
        else
            return "  different";
    }

    private static String trickyMethod(BigInteger value) {
        value = BigInteger.TEN;
        System.out.println("  trickyMethod: " + value);

        if (value.equals(BigInteger.TEN))
            return "  equal";
        else
            return "  different";
    }
}

 

 

And the result

 

Long before: 1
  trickyMethod: 10
  equal
Long after: 1

BigInteger before: 1
  trickyMethod: 10
  equal
BigInteger after: 1

 

POJO demonstration

 

public class Demo {

    static class Container {
        private Long l;

        public Container(Long l) {
            this.l = l;
        }

        @Override
        public String toString() {
            return String.valueOf(l);
        }

        @Override
        public boolean equals(Object obj) {
            Container c = (Container) obj;
            return l.equals(c.l);
        }
    }

    public static void main(String[] args) {
        Container container = new Container(1L);
        System.out.println("Container before: "+ container);
        System.out.println(trickyMethod(container));
        System.out.println("Container after: " + container);
    }

    private static String trickyMethod(final Container container) {
        container.l = 10L;
        System.out.println("  trickyMethod: " + container);

        if (container.equals(new Container(10L)))
            return "  equal";
        else
            return "  different";
    }
}

 

And the result

 

Container before: 1
  trickyMethod: 10
  equal
Container after: 10

 

 

It does not make sense to me because Java passes object reverences via value. That is, Java sends a copy of the original reference. Any changes to this Number's value are invisible outside of the procedure. Thus it makes no difference whether the method modifies or not the value within the method.

Of course, when we give a pojo to a method, we should use the final keyword, but that is a distinct issue.

In the case of Long, why do developers include final keywords in method signatures such as this String method(final Long value)?

 

 

 

asd

0 Kudos
1 Solution

Accepted Solutions
JohannesLindner
MVP Frequent Contributor

Making a method parameter final is mostly a safety feature for the programmer of the method. It doesn't have any effect on calling the method, because (as you demonstrated) changing a parameter inside a method doesn't change it outside the method's scope.

 

So, why make a parameter final? It prevents easily made errors like this:

 

public class Test {
    
    static class MyObject {
        private int id;
        public void set_id(int id) {
            id = id; // Oops, I wanted to set this.id, but I reassigned the parameter!
        }
        public int get_id() {
            return this.id;
        }
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      obj.set_id(123);
      System.out.println(obj.get_id());
    }
}

 

ID: 0

 

Now, this is an easy example, and you would find the error rather quickly, but in long methods it can easily become more complex. If we make the parameter final, we will get a compile time error, so we can fix the error right away:

 

public class Test {
    
    static class MyObject {
        private int id;
        public void set_id(final int id) {
            id = id; // Oops, I wanted to set this.id, but I reassigned the parameter!
        }
        public int get_id() {
            return this.id;
        }
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      obj.set_id(123);
      System.out.println("ID: " + obj.get_id());
    }
}

 

/Test.java:6: error: final parameter id may not be assigned
            id = id; // Oops, I wanted to set this.id, but I reassigned the parameter!
            ^
1 error

 

 Apart from obvious errors like that, reassigning parameters inside the method can be seen as bad practice in general (especially in long, convoluted methods). The final keyword helps the programmer to avoid that.

 

 

As a side note: Your pojo example doesn't apply to the question, because you're not changing the value the parameter points to, but an internal value of the parameter. The final keyword does not prevent this.

A somewhat better example (I hope nobody programs something like this in practice...) would be this, demonstrating again how making a parameter final (and not reassigning parameters to new values) can avoid confusion and errors:

 

public class Test {
    
    static class MyObject {
        private int id;
        public void set_id(final int id) {
            this.id = id;
        }
        public int get_id() {
            return this.id;
        }
    }
    
    static MyObject set_object_id(MyObject obj, int id) {
        obj = new MyObject();
        obj.set_id(id);
        return obj;
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      set_object_id(obj, 123);
      System.out.println("ID: " + obj.get_id());
    }
}

 

ID: 0

 

If line 14 is hidden somewhere in a long and complex method, you or your successor will have to search for a long time to find out why the internal value didn't change.

Now, if we make the parameters in line 13 final, we will get a compile time error. We look at that, realize what we did wrong, and fix it. Making parameters final helps to avoid errors.

 

public class Test {
    // ...
    static MyObject set_object_id(final MyObject obj, final int id) {
        //obj = new MyObject();
        obj.set_id(id);
        return obj;
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      set_object_id(obj, 123);
      System.out.println("ID: " + obj.get_id());
    }
}

 

ID: 123

 

 


Have a great day!
Johannes

View solution in original post

2 Replies
JohannesLindner
MVP Frequent Contributor

Making a method parameter final is mostly a safety feature for the programmer of the method. It doesn't have any effect on calling the method, because (as you demonstrated) changing a parameter inside a method doesn't change it outside the method's scope.

 

So, why make a parameter final? It prevents easily made errors like this:

 

public class Test {
    
    static class MyObject {
        private int id;
        public void set_id(int id) {
            id = id; // Oops, I wanted to set this.id, but I reassigned the parameter!
        }
        public int get_id() {
            return this.id;
        }
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      obj.set_id(123);
      System.out.println(obj.get_id());
    }
}

 

ID: 0

 

Now, this is an easy example, and you would find the error rather quickly, but in long methods it can easily become more complex. If we make the parameter final, we will get a compile time error, so we can fix the error right away:

 

public class Test {
    
    static class MyObject {
        private int id;
        public void set_id(final int id) {
            id = id; // Oops, I wanted to set this.id, but I reassigned the parameter!
        }
        public int get_id() {
            return this.id;
        }
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      obj.set_id(123);
      System.out.println("ID: " + obj.get_id());
    }
}

 

/Test.java:6: error: final parameter id may not be assigned
            id = id; // Oops, I wanted to set this.id, but I reassigned the parameter!
            ^
1 error

 

 Apart from obvious errors like that, reassigning parameters inside the method can be seen as bad practice in general (especially in long, convoluted methods). The final keyword helps the programmer to avoid that.

 

 

As a side note: Your pojo example doesn't apply to the question, because you're not changing the value the parameter points to, but an internal value of the parameter. The final keyword does not prevent this.

A somewhat better example (I hope nobody programs something like this in practice...) would be this, demonstrating again how making a parameter final (and not reassigning parameters to new values) can avoid confusion and errors:

 

public class Test {
    
    static class MyObject {
        private int id;
        public void set_id(final int id) {
            this.id = id;
        }
        public int get_id() {
            return this.id;
        }
    }
    
    static MyObject set_object_id(MyObject obj, int id) {
        obj = new MyObject();
        obj.set_id(id);
        return obj;
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      set_object_id(obj, 123);
      System.out.println("ID: " + obj.get_id());
    }
}

 

ID: 0

 

If line 14 is hidden somewhere in a long and complex method, you or your successor will have to search for a long time to find out why the internal value didn't change.

Now, if we make the parameters in line 13 final, we will get a compile time error. We look at that, realize what we did wrong, and fix it. Making parameters final helps to avoid errors.

 

public class Test {
    // ...
    static MyObject set_object_id(final MyObject obj, final int id) {
        //obj = new MyObject();
        obj.set_id(id);
        return obj;
    }
    
    public static void main(String args[]) {
      MyObject obj = new MyObject();
      set_object_id(obj, 123);
      System.out.println("ID: " + obj.get_id());
    }
}

 

ID: 123

 

 


Have a great day!
Johannes
hectorsalamanca
New Contributor III

Thank you so much johannes

0 Kudos