As several other commentators have mentioned, the problem here is misunderstanding the notion of pass-by-reference. Without getting into the nitty-gritty, as a rule of thumb any time you say a = ...
where a
is an Object
(which includes arrays) you are disconnecting the name a
from a previous value, and pointing it instead at your new value. There is no connection between the two, and no way to reference past values of a
. So in your case, by setting array1
and array2
to new values you are no longer referencing the objects passed into the function. In this case the function arguments are null
, not actual objects, but that doesn't change anything here.
The more important question however is what to do about this? Obviously the current design is unworkable, but how exactly can we do this? It's a common problem, so unsurprisingly there are many viable options.
Use static variables
The easiest thing you can do here is simply ensure both main()
and getData()
are working with the same variable, by using static class variables both methods can reference. While easy for simple projects, this breaks down quickly and starts introducing more problems than it solves as your project's size and scope gets bigger. Use with caution.
public class Demo {
public static void getData(int[] array1, int[] array2) {
array1 = new int[5];
array2 = new int[5];
}
public static void main(String[] args) {
int[] array1 = null;
int[] array2 = null;
getData(array1, array2);
System.out.println(array1[0]); // line 11
}
}
Use a dedicated object, and a pass around references to that object
Alternatively, we can avoid static variables by constructing a single holder object and passing around references to it. We can then freely change the instance variables inside our holder.
public class Holder {
// Should really be private, with constructors/getters/setters
// but for brevity we'll access them directly here. Don't
// take this shortcut in production code
public int[] array1;
public int[] array2;
}
public class Demo {
public static void getData(Holder holder) {
holder.array1 = new int[5];
holder.array2 = new int[5];
}
public static void main(String[] args) {
Holder holder = new Holder();
getData(holder);
System.out.println(holder.array1.length+" "+holder.array2.length);
}
}
Use a resizable data structure
You mention your concern is you don't know ahead of time the size of the loaded data, which makes this a perfect candidate for a List
or other data structure. You also mention you don't want to do this, which is fine, but be really sure you actually need arrays - they provide very few benefits over proper collection types, and many more hassles. Using a resizable data structure lets us construct and populate the same object separately.
import java.util.*;
public class Demo {
public static void getData(List<Integer> ls1, List<Integer> ls2) {
// bad formatting for brevity, don't do this either
ls1.add(1); ls1.add(2); ls1.add(3); ls1.add(4); ls1.add(5);
ls2.add(1); ls2.add(2); ls2.add(3); ls2.add(4); ls2.add(5);
}
public static void main(String[] args) {
List<Integer> ls1 = new ArrayList<>();
List<Integer> ls2 = new ArrayList<>();
getData(ls1, ls2);
System.out.println(ls1.size()+" "+ls2.size());
}
}
Compartmentalize this behavior entirely into a proper object
All of the above are reasonable options given specific use cases, but they all pale in comparison to the robustness, power, and code-safety of creating a proper object and compartmentalizng the behavior entirely. What do I mean by this? Instead of having a getData()
method which applies some business logic to some existing variables, group the variables and the logic together into an object - this is the core tenant of Object Oriented Programming.
public class Data {
// Again, getters/setters, but at least these are final, that's an improvement
public final int[] array1;
public final int[] array2;
public Data() {
array1 = new int[5];
array2 = new int[5];
}
}
public class Demo {
public static void main(String[] args) {
Data data = new Data();
System.out.println(data.array1.length+" "+data.array2.length);
}
}
By using a dedicated class, we hide the logic of building this data from the caller. Instead, the main()
method simply constructs a Data()
object, and can trust simply by constructing the object that it now has all the data it needs. No need to worry about references, array sizes, or anything else; all those decisions are dealt with correctly internally and hidden away from the caller.