This question already has answers here:
Overloaded method selection based on the parameter's real type
(7 answers)
Closed 6 years ago.
The following code showed up in a past paper with multiple mistakes (which i have spotted and fixed easily) but what i struggle to understand is the output.
The classes:
import java.util.*;
class Count {
int i, s, o, l, c;
public Count(Object[] objects){
for(int j=0; j<objects.length; j++){
count(objects[j]);
}
}
public void count(Collection x){c++;}
public void count(List x){l++;}
public void count(Double x){i++;}
public void count(Object x){o++;}
public void count(String x){s++;}
public String toString(){
return c + ", " + l + ", " + i + ", " + o + ", " + s;
}
}
import java.util.LinkedList;
public class Test {
static Object[] objects = {new Double(10), new String("Q1"),
new Object(), new LinkedList()};
public static void main(String args[]){
System.out.println(new Count(objects));
for(Object o : objects)
System.out.println(o);
}
}
Generated output:
0, 0, 0, 4, 0
10.0
Q1
java.lang.Object#6d06d69c
[]
I'd appreciate if someone could explain the output with reference to the code.
Side note: the first line of output is what puzzles me. The other bit i understand.
Last note: This is a unique question regarding the output. It is not a duplicate of any question (to the best of my knowledge - the link to the 'possible duplicate' is regarding method overloading not "how is this output produced?" and the outcomes in both questions are unique to each other) so a precise answer would be helpful. Thanks.
for(int j=0; j<objects.length; j++){
count(objects[j]);
}
corresponds to
public void count(Object x){o++;} only
because the argument you're passing when you call count(objects[j]) is an object. You're calling a count function which has an object as parameter, and that's what Java is doing for you.
The number of objects in objects[] is 4. Hence count(Object x) gets called 4 times.
As regards the other integers, they are initialized by default to 0, and hence you see 0.
That's why you see 0 for all variables except o.
public Count(Object[] objects){
for(int j=0; j<objects.length; j++){
count(objects[j]);
}
In these lines you're creating an array of elements in which each element is of type Object itself (it's the class from which every other class inherits). So output of counting objects of type Object in array shouldn't be a surprise.
(Counting is held by overloaded methods. When you call counting function the one that is chosen must have the most suitable type. Hence, counting with Object type as a parameter is picked.)
0, 0, 0, 4, 0
Next, you're creating new array of Objects, initializing each element of the array with different type.
static Object[] objects = {new Double(10), new String("Q1"),
new Object(), new LinkedList()};
It's possible because of upcasting. As I mentioned before, every element inherits from Object class, so every object in your program is in fact Object. Using mechanism of polymorphism you're program can now "deduce" the real type of elements in your array. Namely, you can see it here
for(Object o : objects)
System.out.println(o);
where Java is printing information obtained dynamically. Hence, double is printed as double, string as string, object itself doesn't have more sensible toString() method than printing its inner "name"(by the way this is the field each object of type Object has) and of course empty link list is no more than [].
One of your problems lies here:
public void count(Collection x){c++;} // never called, init to 0
public void count(List x){l++;} // never called, init to 0
public void count(Double x){i++;} // never called, init to 0
public void count(Object x){o++;} // called 4 times, result is 4
public void count(String x){s++;} // never called, init to 0
You never initialize any of the variables (c, k, i, o, s) so I believe the int type is auto initialized to 0. Then the next time it is called it will increment. So end result is:
0, 0, 0, 4, 0 //
Now the rest:
10.0 // 10.0 because that was value specified when Double was created
Q1 // Q1 because that was the String value when it was created.
java.lang.Object#6d06d69c // memory address of where Object is
[] // initialized to empty list []
The first line outputs the number of Collections, Lists, Doubles, Objects, Strings passed to the constructor of the Count instance in the main method of the Test class, since all of the Count classes only accept one parameter and the count variables are not static four of these will always be zero.
The next four lines print the four objects in the static objects array. This is effectively just printing the results of each objects toSrting() method.
Related
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 8 years ago.
Consider the following code:
public static void resize(int[] x){
x = new int[x.length*2];
System.out.println(x.length + " ");
}
public static void main(String[] args){
int[] x = {1,2};
resize(x);
System.out.println(x.length);
}
The output is "4 2". The question is: I thought that when we are defining an array in the code of the new length, the other array (the previous one with length 2) would be discarded, as now the value of the array points to the "larger" array. So, why would then print out at the end as length 2? I used Arrays.toString to verify, and indeed, the actual values of the array after the void method are {1,2}. This is confusing, as I thought that the array itself would be changed as the value is a pointer to the memory address (in contrast with using the method on a char/int variable, which would not affect the value of the variable).
When you call resize, you pass an array object to the method. This is the basic flow of your program:
initialize array of size 2
pass that array to resize()
resize has a reference to the value of the array
resize points it's reference to a new array twice the size of the old reference
prints "4"
main() prints the size of the initial array "2"
You don't change the original array in the new method, it merely has an array of the same value.
Because Java passes by value, the array doesn't change after your call to resize. (I'm assuming the output is actually 4 2, not 2 4.)
If you want resize to effect a permanent change, you should have the method return x; as its final statement (or use a global variable):
public static int[] resize(int[] x){
x = new int[x.length*2];
System.out.println(x.length + " ");
return x;
}
public static void main(String[] args){
int[] x = {1,2};
x = resize(x);
System.out.println(x.length);
}
The scope of your x variable in resize
public static void resize(int[] x){
x = new int[x.length*2];
}
is local to that function. It does not affect the x that was passed in. This means that as soon as resize completes, that local copy disappears and is eventually garbage collected.
If you want to resize the original array, return it. For example:
public static int[] getNewArrayOfDoubleLength(int orig_length){
return new int[orig_length * 2];
}
And call it with
x = getNewArrayOfDoubleLength(x.length);
Since arrays are immutable, this is a new array. The original one still exists (although it's inaccessible) until it's garbage collected.
This question already has answers here:
What is the ellipsis (...) for in this method signature?
(5 answers)
Closed 7 years ago.
I was looking through some code and saw the following notation. I'm somewhat unsure what the three dots mean and what you call them.
void doAction(Object...o);
Thanks.
It means that this method can receive more than one Object as a parameter. To better understating check the following example from here:
The ellipsis (...) identifies a variable number of arguments, and is
demonstrated in the following summation method.
static int sum (int ... numbers)
{
int total = 0;
for (int i = 0; i < numbers.length; i++)
total += numbers [i];
return total;
}
Call the summation method with as many comma-delimited integer
arguments as you desire -- within the JVM's limits. Some examples: sum
(10, 20) and sum (18, 20, 305, 4).
This is very useful since it permits your method to became more abstract. Check also this nice example from SO, were the user takes advantage of the ... notation to make a method to concatenate string arrays in Java.
Another example from Variable argument method in Java 5
public static void test(int some, String... args) {
System.out.print("\n" + some);
for(String arg: args) {
System.out.print(", " + arg);
}
}
As mention in the comment section:
Also note that if the function passes other parameters of different
types than varargs parameter, the vararg parameter should be the last
parameter in the function declaration public void test (Typev ... v ,
Type1 a, Type2 b) or public void test(Type1 a, Typev ... v
recipientJids, Type2 b) - is illegal. ONLY public void test(Type1 a,
Type2 b, Typev ... v)
It's called VarArgs http://www.javadb.com/using-varargs-in-java. In this case, it means you can put multiple instances of Object as a parameter to doAction() as many as you wants :
doAction(new Object(), new Object(), new Object());
This is the code I have, please look at it before you read the question
package ict201jansem2012;
public class Qn3b {
public static void main(String[] args) {
int a = 1;
int b[] = {4,5};
String s = "Good luck!";
method1(b[1]);
System.out.println("(1) b[0] = "+b[0]+"; b[1] = "+b[1]);
method2(b);
System.out.println("(2) b[0] = "+b[0]+"; b[1] = "+b[1]);
method3(a);
System.out.println("(3) a = " + a );
method4(s);
System.out.println("(4) s = " + s );
}
public static void method1(int b) {
b = 7;
}
public static void method2(int[] b) {
b[0] = 3;
}
public static void method3(int a) {
a = 3;
}
public static void method4(String s) {
s = "UniSIM";
}
}
Output:
(1) b[0] = 4; b[1] = 5
(2) b[0] = 3; b[1] = 5
(3) a = 1
(4) s = Good luck!
So my question is ,
This is intresting for me to know as learning programmer. The int b array 0 index value has changed, but not the other variables like the String s and int a. Before i ran this program I roughly thought in my mind that the variable will change their values as the methods are called ,this is because the method is being called and the main method vairable such as a,s and b array are passed and then they are being modified.
So in a nutshell why is that the b array 0 index is changed while the other variables are not changed?
Because you said you were a beginner programmer, I'll do a little writeup to explain (or try to explain) exactly what is happening.
It is because you are passing an argument to your method1 - method4 methods
These arguments, themselves, are references to other objects.
When you use the assignment operator, an equals sign, you overwrite that reference for the value in the current scope - where variables can be 'seen'.
In your code:
In the case of method1 you are creating a new reference, the variable can only be seen within that scope. That is, when you then go b = << expr >> you are assigning the variable b within method1's scope the value, not b in the main scope. The same is true of your method3 and method4 methods, you are assigning a new value to the respective variables within that scope, as you are creating new references rather than altering the original objects.
But, method2's code behaves differently, this is because you are mutating the object inside that code. You are altering the object directly - rather than creating a new reference inside that scope.
Consider the code below
int[] array = new int[] {1, 2};
public void test()
{
method1(array);
System.out.println(array[0] + ", " + array[1]);
method2(array);
System.out.println(array[0] + ", " + array[1]);
}
// because we are working with objects, the identifier, can be different to the arrays identifier
// in this case, I've chosen to use 'a' instead of 'array' to show this
public void method1(int[] a)
{
// this mutates the array object
a[0] = 2;
}
public void method2(int[] array)
{
// this overwrites the method2.array but not the global array
array = new int[] { 1, 2, 3 };
}
We create a new array, with identifer 'array' in the global scope. (In Java, this would be the classes own scope)
In method1, we take an argument, which is the same object being passed as the global array object, so when we mutate it, both objects will change. So, the first print statement will be
"2, 2"
Where array[0] has been altered
N.B. Because we dealing with objects, the 'name' of the variable doesn't matter - it will still be a reference to the same object
However, in method2, we take an argument, like in method1, but this time we use the assignment operator to assign that variable to a new value in the scope that it's currently in - so the global array isn't altered, so we still print out
"2, 2"
For a beginner programmer, I would personally write a few test programs where you get to fully understand how variables and scopes work.
But just know, everytime you create a new block of code, a new scope is created, local variables to that scope can only be seen in that scope and ones below it.
For instance:
public void test()
{
int a = 5;
method1(a);
System.out.println(a); // prints 5
}
public void method1(int a)
{
// a is only viewable in the method1 scope
// and any scopes below it, that is, any scopes created within method1
// and since we use an assignment operator, we assign method1.a a value, not global 'a' a value
a = 123;
if (true)
{
// this is a new scope, variables created in this block cannot be seen outside it
// but can see variables from above it
System.out.println(a); // prints 123
}
}
Here, we create a new scope inside method1 inside the if statement, which can see a above it. However, because method1 and test's scopes are both independent, when we use the assignment operator, we assign the value of a to the local scope. So a is different in both test and method1
I hope you understand better now.
I'm not very good at conveying things, but if it even helped a little bit in understanding scopes I did well, plus, it was fun.
Java is pass-by-value, but most values (everything that's not a primitive, in this case int[] and String) are references, which means they act like pass-by-reference.
Here's a nice writeup: http://javadude.com/articles/passbyvalue.htm
arrays are special type of objects and memory will be allocated on HEAP. When you pass array as parameter to method it will be pass as reference-value (copy of the reference).
This means initial b and this new reference points to same object. Unless new reference points to another object, changes on this reference will reflect on same object. That is why you are seeing value reflected on original array.
All of the values were passed TO the inner methods, but the inner methods returned nothing. However, method2 modified the internal value of the array that was passed to it, so that inner value appeared modified on return.
Note that method2 is the only one where you did not assign to the variable (parameter) itself, but rather assigned to an element of the object whose reference was passed in.
There is a critical difference between modifying the reference (pointer) to an object, and modifying the object itself.
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.