No coercion in for-each loop?

D

Daniel Pitts

Chronic said:
All engineering is a compromise. For some things casting is better. For
others, using o.toString polymorphism is better. In a vacuum, I cannot say
which would be better. It depends on the design, how the collection is being
used, whether you expect nulls in the collection, whether you control the
things you put in the collection, etc. etc. etc. The o.toString pattern is
one of many that I keep in my arsenal. I find it useful at times.
I didn't suggest using casting at all.
String.valueOf(o) is a null-safe way of handling o.toString(). It still
relies on polymorphism, but handles the "null" reference in a safer way.
 
J

Joshua Cranmer

Dave said:
List list = new ArrayList();
for (String s : list)
System.out.println(s);

It says "Type mismatch: Cannot convert from element type Object to
String". It's fine if I change decl of list to List<String>, or coerce
list to List<String> in the loop decl;

Since many people have already belabored the correct answer to death, I
will attempt to merely provide another avenue for your pondering.

(Definitions, etc., provided from JLS 3 §14.14.2)

Outside the context of the arrays, the foreach loop boils down to this:

for ( VariableModifiers_opt Type Identifier: Expression)

The `Expression', if not an array, must be an object of type `Iterable'.

For Iterable types, the for statement becomes equivalent to this statement:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiers_opt Type Identifier = #i.next();
// Rest of loop
}

where #i is a unique, unused identifier, and I is the type of
Expression.iterator(). So in your case, this is the transformed for loop:

for (Iterator #i = list.iterator(); #i.hasNext(); ) {
String s = #i.next();
System.out.println(s);
}


Since the return type of the raw Iterator.next() function is an Object,
you would have a compile-time cast exception in the transformed for
loop, and therefore you have one in the foreach loop.
The compiler doesn't mind the
code below, even though it causes a runtime type-cast error.

It does mind it: it throws an unchecked warning on the conversion
between list and ar, as required by JLS 3, §5.1.9
> So why can't it insert the cast to String in the loop setup? Am I
> missing something?

From JLS 3, §5.1.9:
Unchecked conversion is used to enable a smooth interoperation of legacy
code, written before the introduction of generic types, with libraries
that have undergone a conversion to use genericity (a process we call
generification).
[...]
While the conversion is unsound, it is tolerated as a concession to
practicality.

Without the generics, the compiler is forced to work off of the raw
return type of List's iterator method: Object. Since the conversion to
String cannot be verified.

Your setup uses inherently unsafe code to begin with: the compiler only
accepts it because to not do so would break too much code (hence the quote).

Since the introduction of foreach and generics go hand-in-hand, there is
no need to tolerate such unsound conversions.
 
C

Chronic Philharmonic

[snip]
I didn't suggest using casting at all.
String.valueOf(o) is a null-safe way of handling o.toString(). It still
relies on polymorphism, but handles the "null" reference in a safer way.

Ok. Sorry.
 
R

Roedy Green

Object o;
String s = o;

won't compile. Why would you expect different behavior in a for loop?

1. Except to the extent that generics are implemented by doing so.

2. autoboxing-unboxing which is can't be done with a cast, but is in
the same spirit.

One of my early flames at Java was the lack of orthogonality in
conversions. I figured if you wanted to specially mark them, either
they should all be done with (convert) or with (anytype).

After experiences with PL/1 which will automatically provide a
conversion from pretty well anything to anything without comment using
the most improbable and practically useless algorithm, I am a firm
believer in explicitly marking conversions. I just think they should
be more symmetrical than French irregular verbs.

I wrote http://mindprod.com/applet/converter.html to help me remember
how to convert a to b. I STILL use it after all these years.
 
B

Ben Phillips

Daniel said:
I didn't suggest using casting at all.
String.valueOf(o) is a null-safe way of handling o.toString(). It still
relies on polymorphism, but handles the "null" reference in a safer way.

Except, of course, if the compile-time type of the expression 'o' here
is compatible with char[] ...
 
M

Mike Schilling

Roedy said:
2. autoboxing-unboxing which is can't be done with a cast, but is in
the same spirit.

One of my early flames at Java was the lack of orthogonality in
conversions. I figured if you wanted to specially mark them, either
they should all be done with (convert) or with (anytype).

After experiences with PL/1 which will automatically provide a
conversion from pretty well anything to anything without comment using
the most improbable and practically useless algorithm, I am a firm
believer in explicitly marking conversions.

Oh yes. In fact, PL/I proved (by trying and failing miserably) that even
among the usual kind of numeric types, there's no way to construct a system
of conversions that's internally consistent and whose results are always
intuitive. Even a type system as simple as XPath 1.0's show this. It has
three scalar types (plus a non-scalar type that's a set of XML nodes.)

1. Numeric (double)
2. Boolean (true/false)
3. String (Unicode characters; can be empty but never null.)

Each has a standard value to which an empty node set automatically
converts:

1. 0.0
2. false
3 the empty string

Conversions among scalar types *almost* work:

1. 0.0 -> false, 0.0 -> empty string
2. false -> 0.0, false -> "false"
3. empty string -> 0.0, empty string -> false

The problem is that the string "false" causes an exception when converted to
a number, and becomes true when converted to a boolean (because it's
non-empty.)
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Mike said:
But Java doesn't insert casts automatically[1], and never has.

Object o;
String s = o;

won't compile. Why would you expect different behavior in a for loop?

Why not ?

:)

To illustrate - in C# the following is not valid:

object o = 123;
int i = o;

but this is:

object[] os = { 123, "abc" };
foreach(int i1 in os)
{
Console.WriteLine(i1);
}

(valid = it compiles, runtime we of course get InvalidCastException,
but if the cast was valid for all elements it would run fine)

You may prefer the Java way over the C# way, but there are
no rule saying that single assignment and for loop need to have
the same casting rules.

Arne
 
M

Mike Schilling

Arne said:
Mike said:
But Java doesn't insert casts automatically[1], and never has.

Object o;
String s = o;

won't compile. Why would you expect different behavior in a for
loop?

Why not ?

:)

To illustrate - in C# the following is not valid:

object o = 123;
int i = o;

but this is:

object[] os = { 123, "abc" };
foreach(int i1 in os)
{
Console.WriteLine(i1);
}

(valid = it compiles, runtime we of course get InvalidCastException,
but if the cast was valid for all elements it would run fine)

Not that I distrust you, but I had to try that for myself to be sure:)
Whoa.
You may prefer the Java way over the C# way, but there are
no rule saying that single assignment and for loop need to have
the same casting rules.

Other than "least astonishment", anyway.
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Mike said:
Arne said:
To illustrate - in C# the following is not valid:

object o = 123;
int i = o;

but this is:

object[] os = { 123, "abc" };
foreach(int i1 in os)
{
Console.WriteLine(i1);
}

(valid = it compiles, runtime we of course get InvalidCastException,
but if the cast was valid for all elements it would run fine)

Not that I distrust you, but I had to try that for myself to be sure:)
Whoa.

It is worth noting that for C# then foreach came before generics.

Implicit casting makes nicer code when generics is not
available.

Arne
 
M

Mike Schilling

Arne said:
Mike said:
Arne said:
To illustrate - in C# the following is not valid:

object o = 123;
int i = o;

but this is:

object[] os = { 123, "abc" };
foreach(int i1 in os)
{
Console.WriteLine(i1);
}

(valid = it compiles, runtime we of course get InvalidCastException,
but if the cast was valid for all elements it would run fine)

Not that I distrust you, but I had to try that for myself to be
sure:) Whoa.

It is worth noting that for C# then foreach came before generics.

Implicit casting makes nicer code when generics is not
available.

True. Now I'm wondering about the following code: (make a prediction
before you try it)

using System;

class MainClass
{
public static void Main(String[] args)
{
object[] os = { 123, 456.23 };
foreach(int i1 in os)
{
Console.WriteLine(i1);
}

}
}





















And the answer is ...... it throws an exception. An implicit cast is done,
but not an implicit conversion.
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Mike said:
object[] os = { 123, 456.23 };
foreach(int i1 in os)
{
Console.WriteLine(i1);
}
And the answer is ...... it throws an exception. An implicit cast is done,
but not an implicit conversion.

I think that is very similar to:

object o = 456.23;
int i = (int)o;

throwing exception but:

object o = 456.23;
int i = (int)(double)o;

working.

Arne
 
M

Mike Schilling

Arne said:
Mike said:
object[] os = { 123, 456.23 };
foreach(int i1 in os)
{
Console.WriteLine(i1);
}
And the answer is ...... it throws an exception. An implicit cast
is done, but not an implicit conversion.

I think that is very similar to:

object o = 456.23;
int i = (int)o;

throwing exception but:

object o = 456.23;
int i = (int)(double)o;

working.

I expect the mechanisms are identical.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top