I am having a little trouble understanding the concept of final in Java.
I have a class that follows:
public class MyClass
{
private int[][] myArray; // intended to be changed
private final int[][] MYARRAY_ORIGINAL; // intended to be unchangable
public MyClass(int[][] array)
{
myArray = array;
MYARRAY_ORIGINAL = array;
}
}
I was under the understanding that final would make MYARRAY_ORIGINAL read only. But I have tried editing myArray, and it edits MYARRAY_ORIGINAL as well. My question is, in this context, what exactly does final do? And for extra credit, how can I copy the array passed through the constructor into MYARRAY_ORIGINAL so that I can have 2 arrays, one to edit, and one that will remain preserved?
Your final MYARRAY_ORIGINAL is indeed read only: you can't assign a new value to the MYARRAY_ORIGINAL reference in other side than class constructor or attribute declaration:
public void someMethod() {
//it won't compile
MYARRAY_ORIGINAL = new int[X][];
}
The values inside the array are not final. Those values can change anytime in the code.
public void anotherMethod() {
MYARRAY_ORIGINAL[0][0] = 25;
//later in the code...
MYARRAY_ORIGINAL[0][0] = 30; //it works!
}
If you indeed need a List of final elements, in other words, a List whose elements can't be modified, you can use Collections.unmodifiableList:
List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3));
The last piece of code was taken from here: Immutable array in Java
In case of Objects, final makes reference can't be changed, but object state can be changed.
That is the reason why you are able to change values of final MYARRAY_ORIGINAL
MYARRAY_ORIGINAL is indeed read only variable. Your array reference can not be assigned a new value nor for their length of the arrays can be changed. A final variables initialization can be deferred till the constructors is called. If one tries to modify the reference of the final variable, compiler will throw an error message. But what is possible is, one can edit the elements of the MYARRAY_ORIGINAL and of the myArray i.e one can change the state of the object assigned to a final variable. For example
Class A {
final int[] array;
public A() {
array = new int[10] // deferred initialization of a final variable
array[0] = 10;
}
public void method() {
array[0] = 3; // it is allowed
array = new int[20] // not allowed and compiler will throw an error
}
}
To understand more on final please take a look at Java Language Specification on final variable.
Final does not mean 'read-only' per se, but more so "safe publication' for other threads than the one to which it is defined. Another aim of 'final' is that it ensures the latest object available in a multi-thread environment.
Secondly, if you define something as "final", for example:
private final int[][] MYARRAY_ORIGINAL;
The reference is "final", but not the object itself. A much better way to understand it would be this:
public static final List myList = new ArrayList();
Now I can access myList from any other threads - I can modify it (add to it); but I cannot
(a) Declare it again - myList = new ArrayList();
(b) Assign it another list - myList = anotherList;
The context for final I would see best, in a multiple-thread scenario.
Bonus: to answer your question, you cannot make a 'readonly' array, you will have to manage that yourself (as final, only maintains 'read-only' to reference not object)
You can use the method System.arraycopy to make a copy of the array as follows -
int[][] source = {{1,2},{3,4}};
int[][] copy = new int[source.length][];
System.arraycopy(source, 0, copy, 0, source.length);
Also, you some problem with your code regarding what you are trying to do. If you look at the constructor
public MyClass(int[][] array) { //something else passes the array
myArray = array;
MYARRAY_ORIGINAL = array; // you are just keeping a reference to it can be modified from outside
}
If you really want nobody to modify the values in that array MYARRAY_ORIGINAL, you should make a copy of the source array that comes comes from outside.
public MyClass(int[][] array) {
myArray = array; //make a copy here also if you don't want to edit the argument array
MYARRAY_ORIGINAL = new int[array.length][];
System.arraycopy(array, 0, MYARRAY_ORIGINAL, 0, array.length);
}
Now you shouldn't have to worry about the array's being modified from outside.
Related
I'm a Java beginner and I don't understand how to make it. When I write in my code something like in the example, my IDE underlines it and says it's wrong when I only started writing my code. Can anybody help me guys?
Example:
public class ArrayUtils {
public static int[] lookFor(int[] array) {
int[] array = {};
}
}
The variable named array is already passed in as a parameter. Which means that you cannot create a new int[] named array inside the java method. Try naming it something else.
Syntax with {} means initialization of your array like int[] array = {1,2,3}.
But you can't initialize the variable with the same name as parameter's name.
You can assign a new array to the variable:
public static int[] lookFor(int[] array) {
array = new int[6]; // assign to variable new array with length 6
array = new int[]{1,3,5}; // assign to variable new array with initialized values
}
Note: in first case all 6 values will be zero
Update: as it was mentioned by #ernest_k reassigning method parameters is a bad practice. To avoid it method parameter usually marked as final int[] lookFor(final int[] array)
I'm very new to Java programming and wanted to try my hand at a little bit outside of my classes. I've created a class that will manipulate arrays, so I set up a private array with no size allocated to it. In a public constructor, how do I set the size of this array?
public ClassName()
{
arr = new int[10];
}
Remember that the Constructor is the method called when an object is instantiated. The Constructor must be a method with no return type and the same name as the class. You could even take in parameters if you'd like to(say a size variable), then create a new array based on the size.
For instance, you could do this:
public ClassName(int size)
{
arr = new int[size];
}
Now when in your tester class, you could create a new object using that constructor.
ClassName c = new ClassName(5);
Which creates a new object with an array of size 5 as a class variable. Hope this helped!
Edit: I should add; if you do not specify a constructor, Java will do it for you, but it will do nothing.
Just like in must of the languages.
example:
anArray = new int[10]; //10 - array size, int is the array type
read about JAVA Arrays
public class foo {
private int a[];
private int b;
public foo(){
a = new int[] {1,2};
b= 3;
}
public int[] getA(){
return this.a;
}
public int getB(){
return this.b;
}
I noticed that it's possible to change a value of A by accessing the object like this:
foo f = new foo();
f.getA()[0] = 5; // f.a[0] changes to 5
but it isn't possible to do something like:
f.getB = 5; // gives error
f.getA() = new int[]{2,3}; //gives error
can someone explain me how this works, and how to prevent the user from changing the value of an array cell?
Thanks in advance.
In Java, array is a reference type, which means that the value of an array expression is a reference to the actual array.
The return value of getA() is, therefore, a reference to the private array inside your object. This breaks encapsulation: you give access to your object's internals.
You can avoid this by either returning a reference to a copy of your internal array, or by providing a different API which only returns individual elements, say a method getA(int index).
f.get(A) returns a reference to an array. You can access that array the way you access any array, and assign values to its elements with f.get(A)[i]=... (though it makes more sense to store the returned array in a variable, which would let you access that array multiple times, without having to call f.get(A) each time).
You can't, however, assign anything f.get(A) via f.get(A)=.., since a method call is not a valid left side of an assignment operator. For all you know, a call to f.get(A) may generate a new array that is not referred to by a member of the foo class, so assigning f.get(A)= new int[5]; would make no sense, since there would be no variable in which to store the new array.
The same explanation applies to f.getB() = 5;.
Instead of giving away the array, to allow the caller to do what they like with it you can use an indexed getter
public int getA(int n){
return this.a[n];
}
public void setA(int n, int x) {
this.a[n] = x;
}
Now, the caller has no access to the array and cannot change it without you knowing.
I'm doing a task for a course in Java programming and I'm not sure how the following thing is working? The method below takes the value from an array and a integer. The integer should be added to the array and then be used outside the method in other methods and so on, but how could this work when the method has no return for the new content of the array? There is a void in the method? Have I missed something? Preciate some help? Is there something about pointers?
public static void makeTransaction(int[] trans, int amount);
Arrays in Java are objects. If you modify the trans array inside the method, the changes will be reflected outside of it1. Eg:
public static void modify(int[] arr)
{
arr[0] = 10;
}
public static void main(...)
{
int x = {1, 2, 3};
System.out.println(x[0]); // prints 1
modify(x);
System.out.println(x[0]); // now it prints 10
}
Note that native arrays can't be dynamically resized in Java. You will have to use something like ArrayList if you need to do that. Alternatively you can change the return type to int[] and return a new array with the new element "appended" to the old array:
public static int[] makeTransaction(int[] trans, int amount)
{
int[] new_trans = Arrays.copyOf(trans, trans.length + 1);
new_trans[trans.length] = amount;
return new_trans;
}
1 It is also worth noting that as objects, array references are passed by value, so the following code has no effect whatsoever outside of the method:
public void no_change(int[] arr)
{
arr = new int[arr.length];
}
You can't add anything to an array. Java arrays have a fixed length. So indeed, what you want to do is impossible. You might make the method return an int[] array, but it would be a whole new array, containing all the elements of the initial one + the amount passed as argument.
If you want to add something to an array-like structure, use an ArrayList<Integer>.
Do you have to keep the method signature as is?
Also, can you be a bit more specific. When you say "the integer should be added to the array", are you referring to the amount argument? If so, then how is that amount added? Do we place it somewhere in the array or is it placed at the end, thus extending the array's length?
As far as pointers go, Java's pointers are implicit, so if you don't have a strong enough knowledge of the language, then it might not be so clear to you. Anyways, I believe that Java methods usually will pass objects by reference, and primitives by value. But, even that isn't entirely true. If you were to assign your object argument to new object, when the method terminates, the variable that you passed to the method is the same after the method executed as it was before. But, if you were to change the argument's member attributes, then when the method terminated those attributes values will be the same as they were inside of the method.
Anyways, back to your question, I believe that will work because an array is an object. So, if you were to do the following:
public static void makeTransaction(int[] trans, int amount)
{
trans[0] = amount;
}
// static int i;
/**
* #param args
*/
public static void main(String[] args)
{
int[] trans = {0,1,3};
makeTransaction(trans, 10);
for(int i = 0; i<trans.length; i++)
{
System.out.println(trans[i]);
}
}
The output of the array will be:
10
1
3
But, watch this. What if I decided to implement makeTransaction like so:
public static void makeTransaction(int[] trans, int amount)
{
trans[0] = amount;
trans = new int[3];
}
What do you think that the output will be? Will it be set to all zero's or will be the same as it was before? The answer is that the output will be the same as it was before. This ties in to what I was saying earlier.
I might've assigned that pointer to a new object in memory, but your copy of the pointer inside of the main method remains the same. It still points to the same place in memory as it did before. When the makeTransaction method terminates, the new int[3] object that I created inside of it is available for garbage collection. The original array remains intact. So, when people say that Java passes objects by reference, it's really more like passing objects' references by value.
I have the following function.
func(ArrayList `<String>`[] name) { ........ }
The function fills the ArrayList[]. (I don't want to return the ArrayList[])
However, in the caller function the ArrayList[] obtained has all ArrayLists as null.
For eg.
name = new ArrayList[num];
func(name);
System.out.println(name[0]);
I get NullPointerException at line 3. Is this because of line 1, i.e. I am not parametrizing? If yes, is there another way this can be done? Because java does not allow creating a generic array of parametrized ArrayList.
That is obviously not your real code, but you're creating an array of ArrayLists, which probably isn't what you want. You can probably just do:
ArrayList<String> name = new ArrayList(num);
func(name);
System.out.println(name.get(0));
Note that when you create the ArrayList, you're only specifying the initial capacity, not the size (number of initial items). It will have an initial size of 0. Your func can just call add to add items.
Even better (no typing errors):
ArrayList<String> name = new ArrayList<String>();
I recommend not bothering with the initial capacity argument (num) - just leave it blank and it will work perfectly. But do bother with the generic type of String in the constructor, or the compiler will complain.
If you want to know how to use the ArrayList (for example, why to use the get() function), you should look at the documentation.
For arrays in Java when you create it all of the elements are either 0, false, or null depending in their type.
So:
final List<String>[] foo;
foo = new ArrayList<String>[10];
foo[0].add("hello"); // crash
that crashes because foo = new ArrayList<String>[10]; allocates enough room to hold 10 ArrayList<String> but it sets all of the values to null. So you need one additional step:
for(int i = 0; i < foo.length; i++)
{
foo[i] = new ArrayList<String>();
}
I haven't compiled the code, but pretty sure it is all correct. You would do that between step 1 and 2 of your program.
I am guessing a bit because your code isn't quite accurate (it would not generate a null pointer as written as near as I can tell).
EDIT:
You would do the new in the method and the for loop with the assignments could be done inside of the method. I prefer to allocate and initialize in the same place (less confusing) but you can split it up if you needed to.
The problem you are encountering is due to the fact that in Java, parameters to methods are passed by value. What this means, is that every parameter is effectively "copied" into the method, meaning that any assignments you make to the parameters are only visible within the method, and cannot be seen by the caller.
Going by your example, you're passing in a null reference to an array of List<String>'s. This reference is then "copied" into the func() method, and when func then assigns something to this variable, it is only the local variable that is being updated, and not the reference held by your calling code.
Here's some compilable code (based on your example) that demonstrates the problem:
public class Main {
public static void main(String[] args) {
List<String>[] array = null;
fill(array);
System.out.println("In main(): " + array[0].get(0));
}
public static void fill(List<String>[] array) {
array = (List<String>[])new List[10];
array[0] = new ArrayList<String>();
array[0].add("test");
System.out.println("In fill(): " + array[0].get(0));
}
}
The println in fill prints the correct value, because the array variable has been assigned to something within the fill method, however the println in the main method throws an NPE because only the "copy" of the array variable was changed by func, and not the "real" variable.
There are two ways to get around this: either instantiate the array within your calling code, or change the fill() method to return a reference to the array is has created.
Below is the first approach:
public class Main {
public static void main(String[] args) {
List<String>[] array = (List<String>[])new List[10];
fill(array);
System.out.println("In main(): " + array[0].get(0));
}
public static void fill(List<String>[] array) {
array[0] = new ArrayList<String>();
array[0].add("test");
System.out.println("In fill(): " + array[0].get(0));
}
}
You may be wondering why this works, because you're still assigning ArrayList's to the elements of the array, however these objects are visible outside of the calling method. The reason for this is that although the fill method is getting a "copy" of the reference to the array, the reference itself is still referencing the same array object. This means that you can modify the internal state of the array object, and any changes you make will be seen by the caller because it referencing that same object.
Below is the second approach:
public class Main {
public static void main(String[] args) {
List<String>[] array = fill();
System.out.println("In main(): " + array[0].get(0));
}
public static List<String>[] fill() {
List<String>[] array = (List<String>[])new List[10];
array[0] = new ArrayList<String>();
array[0].add("test");
System.out.println("In fill(): " + array[0].get(0));
return array;
}
}
(As an aside, you should generally try to avoid creating arrays of generic collections, a better idea would be to use a list to store the lists themselves. E.g:
List<List<String>> list = new ArrayList<List<String>>();
list.add(new ArrayList<String>());
list.get(0).add("test");
new ArrayList<?>[10] give me incompatible type. However, new ArrayList[10] works for me.