Why can I edit the contents of a final array in Java? - java

The following code in Java uses a final array of String.
final public class Main {
public static final String[] CONSTANT_ARRAY = {"I", "can", "never", "change"};
public static void main(String[] args) {
for (int x = 0; x < CONSTANT_ARRAY.length; x++) {
System.out.print(CONSTANT_ARRAY[x] + " ");
}
}
}
It displays the following output on the console.
I can never change
If we try to reassign the declared final array of type String, we cause an error:
final public class Main {
public static final String[] CONSTANT_ARRAY = {"I", "can", "never", "change"};
public static void main(String[] args) {
CONSTANT_ARRAY={"I", "can", "never", "change"}; //Error - can not assign to final variable CONSTANT_ARRAY.
for (int x = 0; x < CONSTANT_ARRAY.length; x++) {
System.out.print(CONSTANT_ARRAY[x] + " ");
}
}
}
Error: cannot assign to final variable CONSTANT_ARRAY.
However, the following code works:
final public class Main {
public static final String[] CONSTANT_ARRAY = {"I", "can", "never", "change"};
public static void main(String[] args) {
CONSTANT_ARRAY[2] = "always"; //Compiles fine.
for (int x = 0; x < CONSTANT_ARRAY.length; x++) {
System.out.print(CONSTANT_ARRAY[x] + " ");
}
}
}
It displays
I can always change
This mean that we could manage to modify the value of the final array of type String. Can we modify the entire array in this way without violating the immutable rule of final?

final in Java affects the variable, it has nothing to do with the object you are assigning to it.
final String[] myArray = { "hi", "there" };
myArray = anotherArray; // Error, you can't do that. myArray is final
myArray[0] = "over"; // perfectly fine, final has nothing to do with it
Edit to add from comments: Note that I said object you are assigning to it. In Java an array is an object. This same thing applies to any other object:
final List<String> myList = new ArrayList<String>():
myList = anotherList; // error, you can't do that
myList.add("Hi there!"); // perfectly fine.

You are misinterpreting the final implementation. final applies to the array object reference, which means once it is initiated, the reference can never change but the array its self can be populated. "Its not violating the rules" you have specified only one rule about the reference change which is working accordingly. If you want the values should also never change you should go for Immutable lists i.e
List<String> items = Collections.unmodifiableList(Arrays.asList("I", "can", "never", "change"));

You can only make it so the array reference can't be changed. If you want the elements to be unable to be changed, you need to use an unmodifiable collection of some kind.

When you declare an array as final, you can change the elements in the array, however you cannot change the reference of this array.

final only guarantees immutability of primitives. And also guarantees that a variable is assigned only once. If an object is mutable you can change the content of it event it defined as final. You may check immutable collections for your needs. Such as Collections.unmodifiableList()
http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util.List)

The reference to the array object is final (can not change e.g. in case you would attempt to associate a different Java array object (instance of String[]) to the same final variable...you'd get a compile time error).
BUT the fields of the final array object in your example are not final, and so you can modify their value. ...while the Java object you created, CONSTANT_ARRAY, after receiving an initial value, will have that value "forever" == until the JVM stops. :) It will be the same String Array instance "forever".
Final variables in Java are not a big deal, just spend some time to digest the topic/idea carefully. :-)
I suggest to all of those who are uncertain to meditate over this page, for example:
https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4
Let me cite the respective part:
"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.
This applies also to arrays, because arrays are objects; if a final variable holds a reference to an array, then the components of the array may be changed by operations on the array, but the variable will always refer to the same array."

The value of the variable CONSTANT_ARRAY cannot change. That variable contains a (reference to an) array. However, the contents of the array can change. Same thing happens when you declare any kind of final variable that is not a simple scalar type (e.g. an object).
Be careful how you name your variables. :-) Calling it a CONSTANT_ARRAY doesn't make the contents of the array unchangeable.
Here's a good reference: The final word on final

When a variable is declared with the final keyword, its value can’t be modified, essentially, a constant. This also means that you must initialize a final variable. If the final variable is a reference, this means that the variable cannot be re-bound to reference another object, but the internal state of the object pointed by that reference variable can be changed i.e. you can add or remove elements from the final array or final collection.

final int[] res;
int[] res1;
int[] res2 = new int[1];
res2[0]=20;
res1=res2;
res1=res2;//no error
System.out.println("res1:"+res1[0]);
res = res2;//only once
//res = res2;//error already initialised
res2[0]=30;
System.out.println("res:"+res[0]);
output::
res1:20
res:30

Related

Why can Java final array variable be modified?

I know that writing a lambda in Java 8 to use a variable requires final type, but why can a variable of final array type be modified?
public static void main(String[] args) {
final String[] prefix = {"prefix_"};
String suffix = "_suffix";
List<Integer> list = Arrays.asList(1005, 1006, 1007, 1009);
List<String> flagList = list.stream().map(param -> {
prefix[0] = "NoPrefix_";
String flag = prefix[0] + param + suffix;
return flag;
}).collect(Collectors.toList());
System.out.println(flagList);
System.out.println(prefix[0]);
}
result:
[NoPrefix_1005_suffix, NoPrefix_1006_suffix, NoPrefix_1007_suffix, NoPrefix_1009_suffix]
NoPrefix_
So a final array means that the array variable which is actually a reference to an object, cannot be changed to refer to anything else, but the members of the array can be modified
refer below link for more information.
https://www.geeksforgeeks.org/final-arrays-in-java/
As per description
for example
final String[] arr = new String[10];
list.stream().map(ele -> {
arr[0] = ele.getName(); // this will work as you are updating member of array
arr = new String[5]; // this will not work as you are changing whole array object instead of changing member of array object.
}).collect(Collectors.toList());
the same thing happen when you use any final collection there.
why can a variable of final array type be modified?
It can't, even though it looks like it can.
The declaration is below, which defines "prefix" as an array:
final String[] prefix
First, edit the code to include two println() calls right after the declaration, like this:
final String[] prefix = {"prefix_"};
System.out.println("prefix: " + prefix);
System.out.println(prefix[0]);
And at the end, add a second println() next to the one you already had, like this:
System.out.println("prefix: " + prefix);
System.out.println(prefix[0]);
If you run that code, you'll see that the object hashCode when printing prefix will be the same object each time. The thing that changes then is not what "prefix" references – that remains the same, it's the same array as before. Instead, what you're doing is changing something inside the array, which is different from the array itself.
Here are the relevant lines from a local run showing that the object reference remains the same, but the value for "prefix[0]" changes:
prefix: [Ljava.lang.String;#5ca881b5
prefix_
prefix: [Ljava.lang.String;#5ca881b5
NoPrefix_
If we try to assign an entirely new array to "prefix", then the compiler will show an error – it knows "prefix" was defined as final.
prefix = new String[]{"new"};
cannot assign a value to final variable prefix
If you're looking for a way to prevent changes to the data, and if it's possible to use a List (instead of an array), you could use
Collections.unmodifiableList():
Returns an unmodifiable view of the specified list. Query operations on the returned list "read through" to the specified list, and attempts to modify the returned list, whether direct or via its iterator, result in an UnsupportedOperationException.

Is a final array of final strings still mutable?

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?
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...
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).
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.
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.
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)

Final String[] and Final String [duplicate]

This question already has answers here:
Why can I edit the contents of a final array in Java?
(9 answers)
Closed 8 years ago.
I am confused. I tried using
final String usr;
and trying to change its value never worked, but when I used an array final String[] usr = {"", ""};, it worked. I even accessed it from this
sgnup.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
user[0] = sUsr.getText();
}
});
I find it very confusing because my understanding of final is that a variable declared with final and add a value to it, the value never changes. But why did an array with final work? I can even set a text to one of those arrays.
Yes, because you don't change the reference of usr but what it contains. final block you from doing something like usr = new String[5]; (change the usr to another array) but the content of the array can be changed without problems.
final String[] a = new String[5];
a = new String[3]; // illegal
final String[] a = new String[5];
a[0] = "Hello"; // legal, i don't change a but what is inside a[0]
In Java, a variable of any object type (including any array) actually is a reference to an object, not the object itself. When you declare a variable to be final, it means that the reference will not change, meaning that the object it references will always be the same object. However, if the internal state of the object can still change without the final modifier, then adding final won't prevent the internal state of the referenced object from changing. In particular for a final array, the array reference itself cannot be reassigned, but the array elements can still change.
In your situation, you can avoid using an array by simply eliminate the final modifier from the String variable.

Why is this one variable being affected?

This is my first question at StackOverflow. This is the code in question:
public class ListStuff {
public static void main(String [] args) {
String[] randomNames = {"Herbie", "Jaco", "Pat", "Michael"};
String[] reversedNames = revertNames(randomNames);
for (int i = 0; i < reversedNames.length; i++) {
System.out.println(reversedNames[i]);
}
}
public static String[] revertNames(String[] s) {
for (int i = 0; i < s.length / 2; i++) {
String tmp = s[s.length - 1 - i];
s[s.length - 1 - i] = s[i];
s[i] = tmp;
}
return s;
}
}
This code runs fine and the reversedNames variable prints as reverted; no complaints there. My main concern, however, is that when I do the String[] reversedNames = revertNames(randomNames);, the variable randomNames also gets reverted. I do in no place change the randomNames variable with a randomNames = blabla;, so I fail to see why that variable keeps changing into a reverted version of itself, even though I am only passing it as an argument.
I have been programming for approximately one year, and my knowledge of variable scopes and such is very limited. Can anyone point me in the right direction?
Arrays in Java are reference types. This means that when you pass your array to the revertNames method, any change inside that method will also be seen outside. Since you are changing the array parameter s inside revertNames with this code:
String tmp = s[s.length - 1 - i];
s[s.length - 1 - i] = s[i];
s[i] = tmp;
the original array randomNames that was passed in place of s is also changed in the process.
That is because you are passing the array by reference, rather than by value. Essentially, this means that the reversedNames array still points to the randomNames array, so changing one will change the other.
Here is a diagram of the variables:
Initially:
randomNames
As we enter the revertNames function:
randomNames <-------- s
The s array still points back to the randomNames array! Thus, when we change s, we change randomNames as well.
As we leave the function:
randomNames <--------- s <--------- reversedNames
Thus, reversedNames points to randomNames.
After the function is finished calling:
randomNames <--------- reversedNames
Although the s array has disappeared, reversedNames still points to randomNames.
To fix the issue, create a temporary variable inside the revertNames function:
public static String[] revertNames(String[] oldarray) {
// Create temporary array to avoid affecting original array
String[] s = oldarray.clone();
...
Within the body of revertNames you have a variable s, this is a reference to an object which is an array. The actual array is the array randomNames. So you are indeed changing values in the source array.
If you use Arrays.copy() you can get an indepedent array to work in - the copy will point to the same strings as the original array, but as Strings are immuatable that's safe.
You can use : StringBuffer:reverse()
some thing like that :
public class t {
public static void main(String [] args) {
String[] randomNames = {"Herbie", "Jaco", "Pat", "Michael"};
StringBuffer rev;
for (int i = 0; i < randomNames.length; i++)
{
rev=new StringBuffer(randomNames[i]);
System.out.println(rev.reverse().toString());
}
}
}
Arrays in Java are reference types, i.e. if you declare
String[] randomNames;
you have declared a local variable that holds a reference to an object that is an array of strings.
The statement
String[] otherNames = randomNames;
will copy the content of variable randomNames to the variable otherNames, but the content is just a reference. That is, this statement causes both otherNames and randomNames to refer to the same array object.
The same happens when you pass a String[] as method parameter. That is, the local variable s in revertNames, and the local variable randomNames in main will contain identical references, i.e. refer to the same array object. That is, the state of that array object will be visible though both s and randomNames.
Strictly speaking, this has nothing to do with scoping, because the variable randomNames is not modified (i.e. it still points to the same array). What is modified is the object it refers to.

Why references to Strings don't behave like other Objects references?

In the following code
public class Test {
public static void main(String[] args){
int [] arr = new int[]{1,2};
String b=new String("abc");
f(b,arr);
System.out.println(b);
System.out.println(arr[0]);
}
public static void f(String b, int[] arr){
b+="de";
b=null;
arr[0] = 5;
}
}
Why the reference variable of the string doesn't behave like the reference variable of the array?.
I know string are immutable so operations on them creates new string but how about references to strings and how the reference b still refer to the old value although it was changed to refer to something else in f() method.
Object references in Java are passed by value. Assigning just changes the value, it does not alter the original object reference.
In your example arr[0] is changed, but try arr=null and you will see it has no effect after the method has returned.
Method call is called by value in Java, well there is long debate about this , But I think that we should consider in terms of implementation language of Java which is C/C++. Object references just pointers to objects, and primitives are the values.. Whenever a method is called, actual arguments are copied to formal parameters. Therefore, if you change pointer to reference another object , original pointer is not affected by this change, but if you change object itself, calling party can also see the changes, because both party are referring the same object..
Well, in your example, you are changing string reference to null in called method, but you are changing object referred by array reference.. These two operations are not the same, therefore they do have different consequences.. for example, if you change the code as below, they would be semantically same operation..
arr = null;
You cnanot change the argument for any method, however you can do the following.
public static void main(String... args) throws IOException {
String[] strings = {"Hello "};
addWorld(strings);
System.out.println("Using an array "+Arrays.toString(strings));
StringBuilder text = new StringBuilder("Hello ");
addWorld(text);
System.out.println("Using a StringBuilder '" + text+"'");
}
private static void addWorld(String[] strings) {
for(int i=0;i<strings.length;i++)
strings[i] += "World!";
strings = null; // doesn't do anything.
}
private static void addWorld(StringBuilder text) {
text.append("World !!");
text = null; // doesn't do anything.
}
prints
Using an array [Hello World!]
Using a StringBuilder 'Hello World !!'

Categories

Resources