Nameless variable declaration - why does it work? - java

I am surprised to see this behaviour.
Is it a bug or something?
for(Object obj = new Object(), Integer = new Integer(300);
obj.toString().length()>3;
System.out.println("on object's loop")) {
} //causes an infinite loop (not foreach loop, of course)
above code compiles and run fine without any reference to new Integer(300). Why so?
I am only interested in knowing why Integer = new Integer(300); is okay without any reference.

Object obj = new Object(), Integer = new Integer(300);
This creates two variables:
obj of type Object, which gets assigned to new Object().
Integer (yes, that's the name of the variable) also of type Object, which gets assigned to new Integer(300).
By the way this has nothing to do with the for-loop; that line would compile fine on its own. Now, if that , was really a ;, it would be a different story.
In general, we can construct valid statements of the form:
Type t1 = ..., t2 = ..., t3 = ..., ...;
which is equivalent to
Type t1 = ...;
Type t2 = ...;
Type t3 = ...;
...

I think he's asking why Integer = new Integer(300) works. – arshajii 2 mins ago
Integer is valid identifier name and its type is Object because of
Object obj = new Object(), Integer = new Integer(300);
Which is equivalent to
int a=2, b=4;
obj.toString() prints the String (consisting classname and hashcode), which has length > 3 so the infinite loop

String String = "foo";
System.out.println(new String(String));
is also legal - the compiler is clever enough to figure out which is a variable reference and which is not.

Related

get method return value

i run following codes in eclipse:
ArrayList<StringBuilder> list = new ArrayList<StringBuilder>();
ArrayList<Integer> alist = new ArrayList<Integer>();
// add some elements ti list
list.add(new StringBuilder("hello"));
list.add(new StringBuilder("2"));
list.add(new StringBuilder("hi"));
list.add(new StringBuilder("this"));
// add some elements to alist
alist.add(4);
alist.add(9);
//get method
StringBuilder a = list.get(3);
a.append(" is a good day");
int b = alist.get(1);
b = 7;
// print the list
System.out.println("LinkedList:" + list);
System.out.println("ArrayList:" + alist);
and result is here
LinkedList:[hello, 2, hi, this is a good day]
ArrayList:[4, 9]
It looks like get method returns a shallow copy of list element (in the case of StringBuilder) to the a, but returns a deep copy (in the case of integer) to the b!
why it happened? Do the get method return a deep or shallow copy of list's elements?
get returns a reference to the List element, not a copy (neither deep nor shallow).
In the first snippet you mutate the object referenced by variable a, so the List is also affected:
StringBuilder a = list.get(3);
a.append(" is a good day");
In the second snippet you assign a new value to the variable b, which doesn't affect the List:
int b = alist.get(1);
b = 7;
In order for your first snippet to behave as the second, you should write:
StringBuilder a = list.get(3);
a = new StringBuilder(" is a good day");
and the List won't be affected.
On the other way, you can't make the second snippet behave as the first. Even if you assigned the List element to an Integer variable, you can't call any method that would mutate it, since Integer is immutable.
In the case of StringBuilder a = list.get(3); you get a reference to element at index 3 assigned to variable a , modifying using a will affect element at index 3. where as int b = alist.get(1); you get a copy of element at 1 assigned to variable b so modifying it will not affect element at 1. there is not connection between b and element at 1 after assignment .
StringBuilder a = list.get(3);
a.append(" is a good day");
int b = alist.get(1);
b = 7;
StringBuilder a = list.get(3);
a.append(" is a good day");
int b = alist.get(1);
b = 7;
Indeed you create a shallow copy of your list element in int b =.... As your List contains Objects of Type Integer you make a new assignment to a primitive integer here, while with the StringBuilder you work on exactly the same object.

java collections stores values or references? [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 8 years ago.
Beginner Java question: when I have
Integer i = 6;
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);
then I write i = 8, ar.get(0) returns 6.
But if I trie the same thing with a class of mine:
class MyC
{
Integer i;
}
MyC myc = new MyC();
myc.i = 6;
ArrayList<MyC> ar = ArrayList<MyC>();
ar.add(myc);
then do myc.i = 8, ar.get(0) returns 8.
Can you please explain this behavior?
The problem has nothing to do with autoboxing, as some answers say.
In the first example you create an Integer and put it in the ArrayList.
Then you change the pointer to the Integer, so that i is pointing to another Integer.
This don't affects the Integer of the ArrayList.
In the second example you create an object and put it in the ArrayList.
Then you change the state of this object by myc.i = 8.
This way the object in the ArrayList is changed.
This is because i = 8 is turned by the compiler into i = new Integer(8), since i is of Integer type, not int, so you have 2 references now. In your second example, there's still one reference referenced from both myc and first element of ar.
As pointed out by Sotirios in comment, if i was primitive, you'll get the same result. But because of slightly different reason:
int i = 6;
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);
i = 8; // changes not a ref, but value on stack
System.out.println(ar.get(0)); // prints out 6
Here you have not two references, but one (to autoboxed Integer inside ar).
All variables, with the exception of primitive types, store references, not values.
First example
Integer i = 6;
Create a new Integer object (lets call it I1) and store a reference to it in i
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);`
Create an ArrayList, and store a reference to I1 in it
i = 8;
Create a new (different) Integer object (lets call it I2) and store a reference to it in i
So now, i = I2, and ar.get(0) = I1
Second example
MyC myc = new MyC();
Create a new MyC (let's call it C) and store a reference to in in myc
myc.i = 6;
Create a new Integer object (lets call it I1) and store a reference to it in C.i
ArrayList<MyC> ar = ArrayList<MyC>();
ar.add(myc);
Create an ArrayList, and store a reference to C in it.
myc.i = 8
Create a new Integer object (lets call it I2) and store a reference to it in C.i
So now myc = C, myc.i = I2, ar.get(0) = C, and therefore ar.get(0).i = I2.
Nothing is referencing I1 and it will be garbage collected.
Integer is an Object-wrapper around the primitive type of int.
In order to store them in a Collection for example List<Integer> list = new ArrayList<Integer>(), the stored element type needs to be a subclass of Object. Therefore, they are stored by reference, as all Objects are stored as references (and all methods receive references by value, see Parameter passing in Java )
It is important to note that in the case of
List<Integer> list = new ArrayList<Integer>();
list.add(5);
int number = list.get(0);
System.out.println("" + number);
The reason why 5 can be used to add the number is because of auto-boxing. When you get the number from the list, it also implicitly calls .intValue() and returns the value of the wrapper as a primitive int. Then in the println() function, the number is implicitly boxed to Integer and then toString() is called.
Pointing i to a different object does not mutate the (original) object. This is not special to primitives. Say you have a List<List<Object>> or a List<List<String>>:
List<List<String>> wrapper = new ArrayList<List<String>>();
List<String> l1 = new ArrayList<String>();
wrapper.add(l1);
l1 = new ArrayList<String>();
l1.add("hello");
for(List<String> list : wrapper)
{
for(String string : list)
{
System.out.println(string);
}
}
If you now iterate over wrapper you find it contains 1 empty list. This is equivalent to your first example. Your second example looks like:
List<List<String>> wrapper = new ArrayList<List<String>>();
List<String> l1 = new ArrayList<String>();
wrapper.add(l1);
l1.add("hello");
for(List<String> list : wrapper)
{
for(String string : list)
{
System.out.println(string);
}
}
In this case the wrapper now contains 1 list with one value in it.
All variables in Java are references that refer to objects that live out on the heap.
The wrapper classes (e.g. Integer) are special cases. Those implement the Flyweight pattern and are immutable.

Java Object class MYSTERY

While Porting a Game I come to a below statement
Object o = new Object[]{"A","B"};
It's really weird!
But when I try the same with "String" then compiler report me an Error msg
String s = new String[] {"A", "B", "C"}; Error: Type mismatch:
cannot convert from String[] to String
Can you please reveal the Mystery of it ?
You have a trivial error in your code. The fact that every class extends Object makes the error more difficult to find.
Since every class (including arrays) extends Object, conversion from A[] to Object is possible.
You wrote int i = new int[] but that's a mistake, you should have written int[] i.
Probably. Object a = new Object[] is not what you wanted to do.
In Java, Array is an Object too. So you can do
Object o = new Object[]{"A","B"};
or
Object o = new String[]{"A","B"};// But array of String not a String
or
Object o = new int[]{1,2};// But array of int not an int
An "Array" of objects is also an object. But an "Array" of ints is NOT an int.
i.e, an int reference cannot point to an Array but an object reference can.
Object[] obj = new Object[5];// works fine
An object can be anything. It can be an array, it can be an single variable.
Object O = new Object[]{"S","A"};
When you define object you can type cast it to your desired data type.
You can not assign an array of a data type to a single variable of that 'same' datatype.
Every array is an Object[] AND an Object, that's why the following are equally valid:
Object[] o = new Object[]{"A","B"};
Object o = new Object[]{"A","B"};
However a String[] is not a String (and vice-versa) and a int[] is not a int (and vice-versa).
Indeed, you would blend primitives with objects in this latter case.

String array instance assigned to single Object

I came across this Java question:
Which one of the following declaration is false:
a. Object[] myarr1 = new String[3];
b. Object myarr2 = new String[3];
c. String[] myarr3 = new String[3];
d. String myarr3[] = new String[3];
I thought it seems obvious that b is the answer. However, the declaration in b is actually correct upon my checking in Eclipse!
Furthermore, I tried the following:
A. Object[] myarr1 = new String[]{"a1","b1","c1"};
B. Object myarr2 = new String[]{"a2","b2","c2"};
C. String[] myarr3 = new String[]{"a3","b3","c3"};
and printed out the content of each array to study what it actually means by assigning an Object to a String array.
System.out.println(myarr1[0]); //print "a1"
System.out.println(myarr2[0]); //error
System.out.println(myarr3[0]); //print "a3"
System.out.println(myarr1); //print address of myarr1
System.out.println(myarr2); //print address of myarr2
System.out.println(myarr3); //print address of myarr3
It seems myarr1 and myarr3 behave like ordinary String arrays, while myarr2 is different. Debugging to look into the content of myarr2 however showed that the content and structure of myarr2 seems to be the same as myarr1 and myarr3.
Can someone explain to me the significance of this? What are we doing when we use declaration b or B. What is the structure of myarr2 and how do I access its elements or cast it as if it is a String array? I tried (String[])myarr2 but that was an error.
Thanks to all.
They all compile, so they are all correct, as of Java 6.
b. Object myarr2 = new String[3];
This works, because everything you create with the new operator is an object in Java. You could later cast it back to its real type, for example these will work as expected:
Object myarr2 = new String[] { "first", "second" };
String[] real = (String[]) myarr2;
real[1] = "hello";
System.out.println(real[1]); // will output "hello"
System.out.println(((String[])myarr2)[1]); // will output "hello"
d. String myarr3[] = new String[3];
This is valid but discouraged as per this doc:
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html
The reason your second print statement emits an error is because the compiler does not know what myarr2 actually is, it just knows it's an Object. For example, you could have had:
Object myarr2 = rand() ? new String[3] : new Dog();
i.e. myarr2 could be some other completely unrelated object as well.
Hence, you cannot index myarr2 as an array. Of course, this can be addressed with a cast:
System.out.println(((String[])myarr2)[0]);

Java generic object with varying types.

Imagine having 2 Generic ArrayLists each storing different types. My program will be using only one of these ArrayLists at a time. Is it possible to create a general ArrayList (currentArrayList) which can store both ArrayLists, and use the two ArrayLists without casting.
ArrayList<Integer> arrInt = new ArrayList<>();
arrInt.add(10);
ArrayList<String> arrString = new ArrayList<>();
arrString.add("ten");
ArrayList<XXX> currentArrayList = arrInt;
Integer i = currentArrayList.get(0);
currentArrayList = arrString;
String str = currentArrayList.get(0);
Thanks.
The problem is the part where you want to:
use the two ArrayLists without casting.
Definitely not going to happen. The whole point of a static type system is to stop you from being able to treat a general type as a specific type without explicitly asking to do so.
So for example, you could say:
ArrayList<?> currentArrayList = arrInt;
Object i = currentArrayList.get(0);
But notice that i is just an Object, so the compiler is happy - we don't know anything about the things in the array except that they must be Object-based because this is a Java program. But you want to say:
ArrayList<?> currentArrayList = arrInt;
Integer i = currentArrayList.get(0);
It's that second line that isn't ever going to happen without a cast.
UPDATE Question from comments:
Can't the compiler easily infer the type of the ArrayList by
looking at the generic type of arrInt ?
Suppose it did that for us. What should it then do with the type of currentArrayList when it sees the line:
currentArrayList = arrString;
Your code as it stands assumes that the compiler is not going to do that kind of inference. Suppose you comment-out that second assignment. Now the compiler could make the inference you suggest, and allow your code to compile. But then in the future if you put back the second assignment, the rest of your code would stop compiling! This would be needlessly confusing.
You have to determine the type of the object anyway eventually.
i guess you want to avoid "supresswarning("casting")" kind of warning?
if ture, here it goes:
ArrayList<Object> list = new ArrayList<Object>();
list.add(1);
list.add("abc");
list.add(new Double(3.1415926));
list.add(true);
for (Object o : list) {
// or more accurate
String type;
if (o instanceof Integer) {
type = "int";
// logic here...
int i = (Integer) o; // no casting warning
} else if (o instanceof String) {
type = "string";
// logic here...
}
// other possibilities if you want
else { // unmatched
type = o.getClass().getSimpleName();
}
System.out.println(type + ": " + o);
}
Misunderstood the question - why on earth would you want to do that?
You are not creating a new ArrayList you are just using a Pointer without the type - its still the same ArrayList!
ArrayList<ArrayList<Object>> array = new ArrayList<ArrayList<Object>>();
array.add(new ArrayList<Integer>());
array.add(new ArrayList<String>());
This should do the trick, since all other possible Objects will inherit from Object in hence be accepted.

Categories

Resources