0

Suppose I have an array

public static final String[] fooArray ={ Foo.a, Foo.b, Foo.c };

where Foo.a b and c are static final Strings.

Could I still do something like fooArray[0] = "taco"; and end up with { taco, Foo.b, Foo.c } as the contents of fooArray?

If so, would making the array private, and having a getter that makes a copy of the array using Arrays.copyOf solve this issue?

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
Orch
  • 887
  • 2
  • 8
  • 21
  • 4
    Try it and see. (spoiler: yes; `final` will prevent `fooArray = new String[] { ... }` but not `fooArray[n] = "whatever"`) – Colonel Thirty Two Apr 15 '15 at 16:34
  • 5
    Why not just try this? – Evan Bechtol Apr 15 '15 at 16:34
  • 1
    possible duplicate of [final array in Java](http://stackoverflow.com/questions/10339930/final-array-in-java) – giorashc Apr 15 '15 at 16:36
  • 2
    better yet, ditch the array and use an unmodifiable list and then you don't need to copy it all the time. – jtahlborn Apr 15 '15 at 16:38
  • Look at Guava's [immutable collections](https://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained) for that. – Giovanni Botta Apr 15 '15 at 16:38
  • It's not a duplicate, as that question doesn't have final Strings. And this is a large product so redeploying for any change takes a considerable amount of time. Thanks for the answers though, very helpful. – Orch Apr 15 '15 at 16:47
  • 1
    Modifiers and annotations always apply to the reference field, not the object this field references. – Peter Lawrey Apr 15 '15 at 17:03

5 Answers5

8

The final applies to the array reference, not its entries. Different strings can still be written to its entries.

If so, would making the array private, and having a getter that makes a copy of the array using Arrays.copyOf solve this issue?

Yes, defensive copies are a fairly standard way to handle this.

Alternately, given what you've outlined, you don't need to have the array at all, just a getter that looks like this:

public String[] getFooArray() {
    return new String[] { Foo.a, Foo.b, Foo.c };
}

Or as jtahlborn commented, use an unmodifiable List<String>:

public static final List<String> fooArray;
static {
    List<String> a = new ArrayList<>();
    Collections.addAll(a, Foo.a, Foo.b, Foo.c);
    fooArray = Collections.unmodifiableList(a);
}
// (There's probably some really nifty Java8 way to do that as a one-liner...
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
4

Yes.

A final array means you can't reassign the array.

So you couldn't do: fooArray = new String[]{...};.

But you can however change what is inside the array. This is the effect of saying: "You can't change the box, but you can change the apples inside the box to be oranges." The box stays the same, and is final, but you've effectively changed the contents.


That being said, if you encapsulate the class, then you can just clone the array when it is needed.

This is currently employed by many classes, such as String#toCharArray and Enum#values, where changing the array's contents comprises the integrity of the finalized object(s).

Obicere
  • 2,999
  • 3
  • 20
  • 31
2

The final-modifier will only prevent changing the fooArray-reference, not the contents of the array itself. Making it private and having a getter returning a copy would hide the original array, and any changes made to the returned array would only affect the copy. However, it would still be possible to modify the original via reflection, but if your intent is to only prevent accidental modification of the original array, that would work.

esaj
  • 15,875
  • 5
  • 38
  • 52
0

Rest have answered about the final well. Just a suggestion on the other part - rather than implementing a getter which does a copy of entire array, if your scenario allows, its better to have a getArrayElement(int position) where you just return an array element rather than the whole array - copying an array is expensive.

Pavan Kumar
  • 4,182
  • 1
  • 30
  • 45
0

You could make a getter that returns a mutable copy of an immutable value. If you used array copy the values inside the copy will still be final.

public class HelloWorld{
    public static void main(String []args){
    System.out.println("Hello World");
    final int b = 5;
    int c = b; // Because of c being mutable we can now change this copy
    c = 7;
    System.out.println(c);
 }
}

... Some psudo code -> for copying an iterable into a mutable form.

public collection<int>(final collection<final int> finalCollection )
    collection nonFinalCollection = new collention();
    for(k : finalCollention){collection.add((int) k)}
    return(collection)
kpie
  • 9,588
  • 5
  • 28
  • 50