1

I have a question about final variables in java and I have written a short code to demonstrate the issue. The Language Specification of java8 states that:

Once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object. Language Specification

To investigate further I have tried out three things in a small example code. The first thing I tried out was to create an object of the type Integer and assign non final reference to it. Then I assigned a final reference to it as well. In the second experiment I did the same with primitive int. Both experiments lead to the same result of me not being allowed by the compiler to increment the final reference, but being allowed to increment the non final reference and in the Output only the non final variable got incremented.

In my third experiment I used a List and assigned, again, a non final and a final reference to the List. Here I was allowed to use the final and the non final reference to call add() and in both references the size was updated.

My test-code:

public void testFunction () {
    Integer nonFinalInteger = 4;
    final Integer finalInteger = nonFinalInteger;

    nonFinalInteger++;
    //Compiler shows error
    //finalInteger++;

    System.out.println("nonFinal Integer: " + nonFinalInteger);
    System.out.println("final Integer: " + finalInteger + "\n");

    int nonFinalInt = 4;
    final int finalInt = nonFinalInt;

    nonFinalInt++;

    //Compiler shows error
    //finalInt++;

    System.out.println("nonFinal primitive int: " + nonFinalInt);
    System.out.println("final primitive int: " + finalInt + "\n");

    List<String> nonFinalVar = new ArrayList<String>();
    final List<String> finalVar = nonFinalVar;

    finalVar.add("Hello");
    //Compiler does not show error
    nonFinalVar.add("World");

    System.out.println("nonFinal List Size: " + nonFinalVar.size());
    System.out.println("final List Size: " + finalVar.size() + "\n");
}

Output:

nonFinal Integer: 5
final Integer: 4

nonFinal primitive int: 5
final primitive int: 4

nonFinal List Size: 2
final List Size: 2

My question is now: Is there a way to protect the state of the object without having to change the code of its class? Lets say we stay in the context that there is a List, where you should not be allowed to add or delete elements. Is it possible to mark the List in a way, so that the compiler would show an error (or at least some kind of warning)?

Edit:

It seems I have caused some confusion with using arrays in the example. My question should refer to any kind of objects, hence I did not put in List in the question or the tags. What I meant was not to tell the compiler that updating the reference is forbidden, I just wanted to know if there was a way of implicitly telling the compiler to reject the calling of functions that may change the state of the object.

Currently the idea with the wrapper class seems to be the most appealing one, even though it still lacks one thing for me: It would not stop me or other people to change the state of the object completely. Final variables always have this "do not touch" trait I am looking here for.

Absent
  • 884
  • 3
  • 11
  • 24
  • 2
    You could put your object in an immutable wrapper class with no setters and a getter that only returns a copy of the original object. The original object can't be changed, although you will need to keep in mind that the copy being returned can sill be changed, – jwitt98 Oct 15 '17 at 14:00
  • 1
    have a look [here](https://stackoverflow.com/questions/23369595/how-to-prevent-element-deleting-from-arraylist-in-java) and [here](https://stackoverflow.com/questions/2419353/make-arraylist-read-only) – Ray Oct 15 '17 at 14:06
  • 1
    You won't be able to make a compile time error because the code will compile successfully for any valid code (such as calling add/remove on a unmodifiable object). You can make a RuntimeException – OneCricketeer Oct 15 '17 at 14:32
  • Too bad a compile time error is not possible. The wrapper solution is not completely satisfactory but it seems like its the one I will have to use. P.S. I have added an edit to the post, I am sorry I confused you, but the question that was linked as duplicate did not provide an answer for my situation – Absent Oct 15 '17 at 15:24
  • I'd go with immutable list classes. They are available in several libraries, for example guava (google common) or eclipse-collections. Although they will not issue a compile-time error when using add function – msrd0 Oct 16 '17 at 00:48

1 Answers1

2

This is a confusing matter. I will try to explain it:

When you declare a variable as final, the result is that his value can be assigned in two different forms:

  • Assigned the value in the same line that you define the final variable.
  • If the final variable B is a property of a class A, then the B's value can be assigned in the constructor of the A class.

But the important thing is, when I'm saying assigned a value I refer to the assignment in the form: final ArrayList<Integet> myArray = new ArrayList<Integer>(Arrays.asList(1,2,3));

Now the variable myArray is always pointing to that arraylist and can't be assigned in the same form again: myArray = new ArrayList<Integer>(Arrays.asList(4,5,6)); will throw an error. But that being said, you can still make operations in that variable. For example, like you see, you could add values doing: myArray.add(4);.

In a few words: when you mark a variable C as final you are doing just that, saying that C can't be assigned to another object, but that target object can change his state at any time.

Ariel Kohan
  • 674
  • 4
  • 15