ternary operator error

J

Jacob

The following produce a compilation error:

int a = 0;
String s = "b" + (a == 0 ? "c" : a);

I can sort of understand why, but on the
other hand it is pretty apparent what I
try to acheive.

Why is the compiler so strict?

Thanks!
 
J

Jon Skeet

Jacob said:
The following produce a compilation error:

int a = 0;
String s = "b" + (a == 0 ? "c" : a);

I can sort of understand why, but on the
other hand it is pretty apparent what I
try to acheive.

Why is the compiler so strict?

You're suggesting that the type of an expression should be different
depending on runtime evaluation - that sounds like a pretty strange bit
of specification. It makes everything simpler if the type of an
expression will always be the same, and it *very* rarely impacts on
real life code, IME.
 
G

Gordon Beaton

The following produce a compilation error:

int a = 0;
String s = "b" + (a == 0 ? "c" : a);

I can sort of understand why, but on the
other hand it is pretty apparent what I
try to acheive.

Why is the compiler so strict?

Every expression must have a type that is unambiguous and can be
evaluated at compile time. That means that both branches of the
conditional operator must evaluate to the same type, so that the
expression itself can be assigned a type that can be used when the
surrounding expression is evaluated.

The compiler sees something like this:

String <-- String + E

where E is

(boolean ? String : integer)

So what is the type of E?

An integer and a String are not the same type, and what you
*subsequently* do with the resulting value (i.e. in the enclosing
expression) does not change that fact. A type for E cannot be assigned
at compile time.

Consider that the compiler might need to emit different code depending
on the whether the conditional expression evaluates to an integer or a
String.

You can help it though:

String s = "b" + (a == 0 ? "c" : Integer.toString(a));

/gordon
 
T

Tomy

Jacob said:
The following produce a compilation error:

int a = 0;
String s = "b" + (a == 0 ? "c" : a);

I can sort of understand why, but on the
other hand it is pretty apparent what I
try to acheive.

Why is the compiler so strict?

Thanks!


Because java specification is strict in that way.
Type of the data must be known which is not
the case here since the result of your tenary
(in the way you want to use it) cannot be known
at compile time since it depends on value of a.

It is the same reason which prohibits you from
writing a method which can return either int
or String.

This might be solved in 1.5 with autoboxing-unboxing
feature. I'm just guessing here....
 
T

Tomy

Marco Schmidt said:
Tomy:

[...]
This might be solved in 1.5 with autoboxing-unboxing
feature. I'm just guessing here....

AFAIK that feature will only lead to easier handling of primitive
types and their respective wrapper classes, e.g. int and Integer.

Transparent conversion between int and String is a different thing.


Not really I think in this example....
"b" + (a == 0 ? "c" : a)
"c" is String
a is int, gets implicitely boxed in Integer....
Tenary expression has type of Object....
"b" + Object is legal so everything OK....

I'm not sure things will work this way, but they might
since ternary expression now has "a type that is unambiguous and can be
evaluated at compile time" Object type.... (Thank you Gordon on this phrase,
my english
is not so good ).....

Tomy.
 
J

Joona I Palaste

Tomy said:
begin 666 smile.gif
M1TE&.#EA#P`/`)$!`````+^_O___`````"'Y! $```$`+ `````/``\```(N
MC V9QY$"X6(@6GGJO0!)+3RA$XDA:&Y6JGXMIX$K%G,8^2EE]G:4?&ID%+Y#
#`0`[
`
end
begin 666 frown.gif
M1TE&.#EA#P`/`)$``````+V]O8RM_[V]O2'Y! $```,`+ `````/``\```(O
MG V9QY,"X6) QBK P?A*E$5BI76/9*:7*K:K"Y^H,CK6^-GPM>U9(T,U-@H-
$HP``.P``
`
end

Could you please stop with this MIME stuff, thanks?

--
/-- Joona Palaste ([email protected]) ---------------------------\
| Kingpriest of "The Flying Lemon Tree" G++ FR FW+ M- #108 D+ ADA N+++|
| http://www.helsinki.fi/~palaste W++ B OP+ |
\----------------------------------------- Finland rules! ------------/
"Hasta la Vista, Abie!"
- Bart Simpson
 
P

pete kirkham

Jon said:
A better way is
String s = "b" + (a == 0 ? "c" : String.valueOf(a));

the source code for String.valueOf is:

public static String valueOf(int i) {
return Integer.toString(i, 10);
}

so you only end up making an extra method call, so for code where you've
decided that a is an integer the integer version is more efficient.

In this particular situation the compiler doesn't have to know the type
as the + operator is expanded to calls to StringBuffer, so:

String s = "s" + ( test ? X : Y);

gets expanded to something like:
StringBuffer _temp = new StringBuffer();

_temp.append("s");

String _temp2;

if (test) goto :1
_temp2 = Y;
goto :2
:1
_temp2 = X;
:2

_temp.append(_temp2);

String s = _temp.toString();

(_temp<x> being either a virtual local variable or a stack value)

but it /could/ be expanded to:
StringBuffer _temp = new StringBuffer();

_temp.append("s");

if (test) goto :1
_temp.append(Y);
goto :2
:1
_temp.append(X);
:2

String s = _temp.toString();

with the types of X and Y only being used to determine which append call
to make, which loosens the constraint that X and Y be the same type.

this wouldn't break anything, and the + operator for strings is
different from other operators anyway.

(though since StringBuffer.append(int) calls String.valueOf(int) which
calls Integer.toString(int, int) it saves one indirection to use
StringBuffer.append(Integer.toString(int)) rather than
StringBuffer.append(int))


Pete
 
D

Dale King

Jon A. Cruz said:
A better way is
String s = "b" + (a == 0 ? "c" : String.valueOf(a));


The best way is:

String s;

if( a == 0 )
{
s = "bc";
}
else
{
s = "b" + a;
}
 
M

Marshall Spight

Gordon Beaton said:
The compiler sees something like this:

String <-- String + E

where E is

(boolean ? String : integer)

So what is the type of E?

In a different language, the answer might be "String or int" which
actually doesn't seem like all that unreasonable of an answer.


Marshall
 
J

Jon A. Cruz

pete said:
the source code for String.valueOf is:

No. Not "the" source. "A source"

Other JVM's (including many of IBM's, which are often top performers)
use the fact that the String class can access the internals of the
String class (while the Integer class can not) to optimize if needed.

Remember, the language spec only said it behaves "as if"
Integer.toString() is called, not that it must call it.
so you only end up making an extra method call,

Myabe, or maybe not. A lot depends on the specific VM's implementation.

so for code where you've
decided that a is an integer the integer version is more efficient.

Ahh.. But also...

If you happen to change the declaration of the variable ('a' in this
case) to some other type (long for example), then Integer.toString() is
broken. However, String.valueOf() will work for boolean, for char, for
char[], for double, for float, for int, for long and for Object.

That also makes the code more OO friendly.



(though since StringBuffer.append(int) calls String.valueOf(int) which
calls Integer.toString(int, int) it saves one indirection to use

Or String.valueOf() uses an optimized version that deals with the
internals of the String class directly and gets much better performance.
Again, that detail all depends on the VM being used.
 
M

Marco Schmidt

Tomy:
Not really I think in this example....
"b" + (a == 0 ? "c" : a)
"c" is String
a is int, gets implicitely boxed in Integer....
Tenary expression has type of Object....
"b" + Object is legal so everything OK....

OK, in this context the autoboxing feature could work. However, I
don't really know how this feature is implemented in the compiler. If
the fact that a variable X is an int produces an error, will the
compiler start again and treat X as an Integer?

Regards,
Marco
 
P

pete kirkham

Jon said:
No. Not "the" source. "A source"

Even so, but you were saying '~blah~ is better than ~wibble~' not saying
'~blah~'s better because some VM with undefined internals may implement
it in a more optimal manner than the reference implementation of ~wibble~'.
Other JVM's (including many of IBM's, which are often top performers)
use the fact that the String class can access the internals of the
String class (while the Integer class can not) to optimize if needed.

I've taken a quick look at IBM's java site and can't find anything like
that. Where did you get this information? Why on earth don't they use
the same optimized code for both: there's such thing as 'private' in
machine code? Why do they change the calling pattern so as to break
mixed-use with other libraries?
Remember, the language spec only said it behaves "as if"
Integer.toString() is called, not that it must call it.

Hence the quote from the reference implementation, not the API
Ahh.. But also...

If you happen to change the declaration of the variable ('a' in this
case) to some other type ..

Yes, if you haven't decided than a is an integer, then the other
approach is more flexible. Such changes should idealy be done by a tool
not the programmer.
That also makes the code more OO friendly.

Personally, I think what would make the code more OO friendly would be
to turn type checking off where the result is being passed to a form
which doesn't care what the type is, as in the StringBuffer calls I
posted. The Java language tries to be both OO and strongly typed, and so
falls into the mud between.


Pete
 
P

pete kirkham

Jon said:
Ok.

It is better because:

1) It is clearer as to the intent. Most experts say to write for clarity
first.

I'd say that writing "s" + (a==0 ? "foo" : a) is clearer to the intent.
2) It is an 'optimization' that usually doesn't make any noticeable
difference, and is very commonly premature.

So use a tool to optimize: a lot of Java would greatly benefit from a
preprocessor/macro expander. If you put the optimization into a tool,
that expands a generic form, then you save time optimizing by hand.
3) It helps reduce bugs.
4) It makes the code hold up even if the type of the variable changes.
5) It allows the code to be copy-n-pasted and reused without having to
be changed. Reduces rogue tile occurances.

They are identical in this respect.
6) On the fastest VM's, doing it that way can be faster. (although you
should note point 2)

Is the IBM VM faster when running on OS X? I couldn't find any benchmarks ;)
...

There, is that better?

I think you missed the point, and I maybe would have been better to post
without the aside on the extra method indirections; the main gain I was
driving at was to allow a tool to expand the generic form where deciding
the type can be left until the expression is used (given that changing
the language seems a bit optimistic). OK, so if you're targeting one
particular vendor's JVM, then a small gain can be made by customizing
the expansion in a different way than what is better for the standard one.
You seem to misunderstand. It's all in Java-land, not native land. Also
it does nothing to break use, and does not introduce any other problems
to do so.

Yes, not having had any reason to inspect the 1.1 implementation of the
IBM libraries, I misunderstood your use of internal, and assumed it was
internal to the JVM implementation not it's libraries.
The reference implementation is just to be consulted on points on which
the language spec does not cover. Since it does in fact cover this
point, the reference implementation does not trump it.

Neither the language spec nor the API spec cover whether it's any
'better'. The reference implementation does indicate that it's
(marginally) better to expand one way rather than the other in terms of
calls, so I chose to do it that way, which AFAI can tell is better on
the JVMs I get to use.
I disagree. If you need to change a variable declaration, it should not
require a tool to assist you to do so. If that is the case, it sounds
like your code is probably too fragile and unmaintainable.

In the absence of a customisable compiler, writing a tool that expands

<String> + (<bool> ? <type A> : <type B>)

into the StringBuffer calls I suggested would make it less fragile, not
more, for reasons you've already pointed to.

Such a tool should also be able to pick up on forms such as

String s = "foo";

if (a != 3) {
s += a;
}

s += "bar";

Which could use only the one StringBuffer instead of the two that javac
does (the one in the OS X JDK, maybe IBM's compiler already has this
covered, I don't know, they don't do OS X developer kits).


Pete
 

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

Staff online

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top