Forums Register Login

Regarding generics, mixed with legacy collection code.

+Pie Number of slices to send: Send

Consider the following...



Now... imagine this was an exam question... Options could be:

1: Code compiles and runs, printing first the Integer then the String inside the list.
2: The code compiles but throws a runtime exception.
3: The code fails to compile.

Right... this is purely my own creation, inspired from a textbook question. The problem for me here, is that if you run this code, there will be a ClassCastException thrown at runtime. The underlying question is, why does this happen when the enhanced for loop is expecting (casting) the instances in the list to Object and not Integer?

At first I thought it was because 1: the list is created as an <Integer> generic type, meaning that the compiler automatically inserts casts to Integer whenever we try to extract something from it. This together with 2: the enhanced for loop really uses the Interator mechanic, and since the generic type of the list is <Integer>, then so is the Iterator (working behind the scenes of the enhanced for loop). Meaning there is an invisible cast to Integer by the invisible Iterator, behind the for loop.

BUT (and here's what really confuses me), if we remove the for loop, and uncomment the Iterator lines above it, the code runs fine! (compiles with warnings due to the add() method, but it runs perfectly and prints the objects). Even though the Iterator is defined as having generic type <Integer> !

The 2 questions this boils down to are...

1: Why does the enhanced for loop cause an exception, even though we're casting the objects to Object?
2: Why does the Iterator NOT throw an exception, even though it is defined as generic type <Integer>? I think this should imply a behind the scenes casting to Integer, but obviously it doesn't?

// Andreas


+Pie Number of slices to send: Send
 

Andreas Svenkson wrote:
Consider the following...



Now... imagine this was an exam question... Options could be:

1: Code compiles and runs, printing first the Integer then the String inside the list.
2: The code compiles but throws a runtime exception.
3: The code fails to compile.

Right... this is purely my own creation, inspired from a textbook question. The problem for me here, is that if you run this code, there will be a ClassCastException thrown at runtime. The underlying question is, why does this happen when the enhanced for loop is expecting (casting) the instances in the list to Object and not Integer?

At first I thought it was because 1: the list is created as an <Integer> generic type, meaning that the compiler automatically inserts casts to Integer whenever we try to extract something from it. This together with 2: the enhanced for loop really uses the Interator mechanic, and since the generic type of the list is <Integer>, then so is the Iterator (working behind the scenes of the enhanced for loop). Meaning there is an invisible cast to Integer by the invisible Iterator, behind the for loop.

BUT (and here's what really confuses me), if we remove the for loop, and uncomment the Iterator lines above it, the code runs fine! (compiles with warnings due to the add() method, but it runs perfectly and prints the objects). Even though the Iterator is defined as having generic type <Integer> !

The 2 questions this boils down to are...

1: Why does the enhanced for loop cause an exception, even though we're casting the objects to Object?
2: Why does the Iterator NOT throw an exception, even though it is defined as generic type <Integer>? I think this should imply a behind the scenes casting to Integer, but obviously it doesn't?

// Andreas



Andreas wrote: The problem for me here, is that if you run this code, there will be a ClassCastException thrown at runtime.



Jack wrote: Hello Andreas, I have just passed this code to my eclipse, it compiled an ran perfectly!... In three ways, independently and together, the code compiled and ran perfectly. I guess I still dont have a perfect understanding of mixing legacy and non-legacy ,
I will appreciate it if someone can help, because at this point I am a little confused

 
+Pie Number of slices to send: Send
It looks as if the enhanced for statement casts the references internally, before it assigns them to the variable. I'm not certain though.
+Pie Number of slices to send: Send
 

Stephan van Hulst wrote:It looks as if the enhanced for statement casts the references internally, before it assigns them to the variable. I'm not certain though.



Jack wrote:Hello Stephan, your thought is as good as mine, however runtime exception is thrown when you attempt to loop through the collection with a specific type, example if you change the 'Object o' inside the for loop to 'int i', my thinking is that both the iterator and the for loop have similar 'modus-operandi' in terms of getting out the elements from the collection.

 
+Pie Number of slices to send: Send
 

Stephan van Hulst wrote:It looks as if the enhanced for statement casts the references internally, before it assigns them to the variable. I'm not certain though.



Yes indeed that was my thinking too but I thought that the enhanced for-loop in actuality uses an Iterator in the background, so it doesn't explain why using an Iterator doesn't cause the same exception.

Ikpefua I have no idea how it can work for you, have you tried compiling and running it from the command line, outside of eclipse? Personally I still use NetBeans, the transition to Eclipse will have to wait a bit ;)

EDIT: Whether or not the enhanced for loop uses an Iterator in the background aside, it also really bothers me that we can use an Iterator explicitly with a specified generic type of Integer and not get an exception at runtime when we hit the String.

// Andreas
+Pie Number of slices to send: Send
 

Andreas Svenkson wrote:EDIT: Whether or not the enhanced for loop uses an Iterator in the background aside, it also really bothers me that we can use an Iterator explicitly with a specified generic type of Integer and not get an exception at runtime when we hit the String.


That's because of type erasure - the generic information simply doesn't exist at runtime. Which is why it's important to avoid mixing generic and non-generic code if at all possible.
+Pie Number of slices to send: Send
 

Andreas Svenkson wrote:

Stephan van Hulst wrote:It looks as if the enhanced for statement casts the references internally, before it assigns them to the variable. I'm not certain though.



Yes indeed that was my thinking too but I thought that the enhanced for-loop in actuality uses an Iterator in the background, so it doesn't explain why using an Iterator doesn't cause the same exception.

Ikpefua I have no idea how it can work for you, have you tried compiling and running it from the command line, outside of eclipse? Personally I still use NetBeans, the transition to Eclipse will have to wait a bit ;)

EDIT: Whether or not the enhanced for loop uses an Iterator in the background aside, it also really bothers me that we can use an Iterator explicitly with a specified generic type of Integer and not get an exception at runtime when we hit the String.

// Andreas



Andreas wrote: it also really bothers me that we can use an Iterator explicitly with a specified generic type of Integer and not get an exception at runtime when we hit the String.


Jack wrote:Andreas, it bothers me too, I had to cancel an appointment (willingly) to study this problem, the definition of an iterator(according to the K & B Book Chapter 7, page 580) says "An Iterator is an OBJECT that is associated with a specific collection" that brings us back again to what we initially said, since an Iterator is an 'OBJECT' and 'Object o' inside the for loop is an 'OBJECT' they have a similar 'Modus-Operandi' 'behind-the-scenes', INDEPENDENTLY of the declared generic type <Integer>. Its like both loops are saying: "cast ANYTHING you bring out from the collection to Object".
And one more thing I just remember about the concept of 'Generic-Types'; "At Runtime The JVM Sees The Iterator Like This:"
and NOT like this:

Remember that looping is a runtime event.
I hope this theory is correct, otherwise I guess we need some help here. ...@Andreas...@Stephan, please tell me what you think.

 
+Pie Number of slices to send: Send
OK, I've done a bit of experimenting with different versions and looking at the byte code (javap -c, for those that are interested). My conclusion is that this:
Produces identical byte code to this:
So that's what the compiler is doing with the enhanced for loop - and you can quite clearly see where the failing cast occurs,
+Pie Number of slices to send: Send
 

Matthew Brown wrote:OK, I've done a bit of experimenting with different versions and looking at the byte code (javap -c, for those that are interested). My conclusion is that this:
Produces identical byte code to this:
So that's what the compiler is doing with the enhanced for loop - and you can quite clearly see where the failing cast occurs,



Not really! Compiler simply replace enchanced for loop by traditional for loop.so compiler convert the mentioned enchanced for as below.

so below code runs fine! though you can mix geniric with non-generic for backward compability, as matthew mentioned above avoid the mix/mess!

+Pie Number of slices to send: Send
 

Matthew Brown wrote:

Andreas Svenkson wrote:EDIT: Whether or not the enhanced for loop uses an Iterator in the background aside, it also really bothers me that we can use an Iterator explicitly with a specified generic type of Integer and not get an exception at runtime when we hit the String.


That's because of type erasure - the generic information simply doesn't exist at runtime. Which is why it's important to avoid mixing generic and non-generic code if at all possible.



Well that's possibly the explanation. However I was under the impression that the compiler is supposed to handle generic collections by automatically inserting casts when we retrieve something from such a collection. But perhaps that's only the case if we had an explicit variable assignment, as such:



But I still think it would make more sense to have an invisible cast whenever the Iterator.next() method is invoked.

// Andreas
+Pie Number of slices to send: Send
 

Matthew Brown wrote:OK, I've done a bit of experimenting with different versions and looking at the byte code (javap -c, for those that are interested). My conclusion is that this:
Produces identical byte code to this:
So that's what the compiler is doing with the enhanced for loop - and you can quite clearly see where the failing cast occurs,



Interesting! I should have read all responses before posting

And assuming this is right, then would you agree that the invisible cast inserted by the compiler is ONLY inserted when you actually assign the it.next() value to a variable ? It certainly would seem that way to me since it doesn't appear to be inserted if we just send the value of Iterator.next() to S.O.P.

// Andreas
No one can make you feel inferior without your consent - Eleanor Roosevelt. tiny ad:
a bit of art, as a gift, the permaculture playing cards
https://gardener-gift.com


reply
reply
This thread has been viewed 1199 times.
Similar Threads
How do iterate 2 list's using for-each loop(enhanced for loop)
ArrayList with Iterator new*
Did the For loop lie
Enhanced for loop
Using for..each loop on Object[]
More...

All times above are in ranch (not your local) time.
The current ranch time is
Mar 28, 2024 23:09:16.