what is the sense of final ArrayList?

Which advantages/disadvantages we can get by making ArrayList (or other Collection) final? I still can add to ArrayList new elements, remove elements and update it. But what is effect making it's final?

90429 次浏览

But what is effect making it's final?

This means that you cannot rebind the variable to point to a different collection instance:

final List<Integer> list = new ArrayList<Integer>();
list = new ArrayList<Integer>(); // Since `list' is final, this won't compile

As a matter of style, I declare most references that I don't intend to change as final.

I still can add to ArrayList new elements, remove elements and update it.

If you wish, you can prevent insertion, removal etc by using Collections.unmodifiableList():

final List<Integer> list = Collections.unmodifiableList(new ArrayList<Integer>(...));

It just means that you can't re-assign its reference. Attempting to do something like the below will lead to compiler error.

final List<String> list = new ArrayList<String>();


list = new LinkedList<String>();
^
Compiler error here

If you really want an immutable list, you should use the Collections.unmodifiableList() method.

You won't be able to modify its reference using new ArrayList for example.

Final is a keyword or reserved word in java and can be applied to member variables, methods, class and local variables in Java. Once you make a reference final you are not allowed to change that reference and compiler will verify this and raise compilation error if you try to re-initialized final variables in java.

It doesn't effect what you can do with the ArrayList as you rightfully observe - the ArrayList itself is still mutable. You have just made the reference immutable.

But making a variable final does have other benefits:

  • It prevents the variable from being changed if it is expected to stay constant. This can help prevent future bugs.
  • Making variables final can help the compiler make certain performance optimisations.

In general, the more things that you make immutable, the better. So making references final (even if they are references to mutable objects) is generally a good idea.

You cannot rebind it to a different collection as aix said.

Example: In conjuction with immutable list implementation you get safe members which you can make public available.

Example: When you rely on that reference does not change you need final. This is true in synchronization scenarios for example.

There may be much more examples. It is good style to declare members final if you do not intend to change the reference at all.

Making the variable final makes sure you cannot re-assign that objest reference after it is assigned. As you mention you can still use that lists methods to make changes.

If you combine the final keyword with the use of Collections.unmodifiableList, you ge the behaviour you are probably trying to achieve, for instance:

final List fixedList = Collections.unmodifiableList(someList);

This has as result that the list pointed to by fixedList cannot be changed. Beware however that it can still be change through the someList reference (so make sure it is out of scope after this asignment.)

I personally mark the collections field of my classes as final to save the users of my class from checking whether it is null or not. This works because, once the value is already assigned to a final variable, it can never be reassigned to another value, including null.

final has a lot of consequences under multi threading.

  1. JMM clearly defines a final field's initialization completion is guaranteed.

What's not clearly defined is:

  1. Compilers are free to reorder them across memory barriers.
  2. Compilers can always read a cached copy.

To get a really immutable list, you will have to make deep copies of the contents of the list. UnmodifiableList would only render the list of references somewhat immutable. Now making a deep copy of the List or array will be tough on memory with the growing size. You can make use of serialization/deserialization and store the deep copy of array/list into a temp file. The setter would not be available as the member varaible needs to be immutable. The getter would serialize the member variable into a file and then desialize it to get a deep copy. Seraialization has an innate nature of going into the depths of an object tree. This would ensure complete immutability at some performance cost though.

 package com.home.immutable.serial;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;


public final class ImmutableBySerial {


private final int num;
private final String str;
private final ArrayList<TestObjSerial> immutableList;


ImmutableBySerial(int num, String str, ArrayList<TestObjSerial> list){
this.num = num;
this.str = str;
this.immutableList = getDeepCloned(list);
}


public int getNum(){
return num;
}


public String getStr(){
return str;
}


public ArrayList<TestObjSerial> getImmutableList(){
return getDeepCloned(immutableList);
}


private ArrayList<TestObjSerial> getDeepCloned(ArrayList<TestObjSerial> list){
FileOutputStream fos = null;
ObjectOutputStream oos = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
ArrayList<TestObjSerial> clonedObj = null;
try {
fos = new FileOutputStream(new File("temp"));
oos = new ObjectOutputStream(fos);
oos.writeObject(list);
fis = new FileInputStream(new File("temp"));
ois = new ObjectInputStream(fis);
clonedObj = (ArrayList<TestObjSerial>)ois.readObject();


} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return clonedObj;
}
}

Essentially what you are trying to achieve here is making the list immutable I guess. However when you mark the list reference final it implies that the reference cant be pointed to any other list object other than this.

If you want a final (immutable) ArrayList go for Collections class's utility method Collections.unmodifaibleList( list ) instead.

I came to think of this same question and coded and example to add to the explanation from yet another angle.

The final arrayList can still be modified, refer to the example below and run it to see for your self.

Here is the immutable class with immutable List declaration:

public final class ImmutableClassWithArrayList {
final List<String> theFinalListVar = new ArrayList<String>();
}

And here is the driver:

public class ImmutableClassWithArrayListTester {
public static void main(String[] args) {
ImmutableClassWithArrayList immClass = new ImmutableClassWithArrayList();
immClass.theFinalListVar.add("name");
immClass.theFinalListVar.forEach(str -> System.out.println(str));
}
}

As you can see, the main method is adding (modifying) the list. So the only thing to note is that the "reference" to the object of the collection type can't be re-assigned to another such object. As in the answer by adarshr above, you can't do immClass.theFinalListVar = new ArrayList(); in the main method here.

The modification part really helped me understand this and hope it helps in same way.

If We declare ArrayList as final we can still add elements to it but we can't reassign it.

final ArrayList<String> list = new ArrayList<>();
list.add("abc"); //allowed
list.add("pqr"); //allowed


list = new ArrayList<>(); // Not allowed