Statements inside ?: expression - java

I'm learning scala and trying to understand the following sentence:
In Java you can't put statements inside a ?: expression
Can someone explain this to me, perhaps with an example and maybe in the context of Scala?

Java has a ternary conditional operator inherited from C, it looks like this:
int x = some_condition ? 1 : 2;
x will be equal to 1 if some_condition is true, and to 2 otherwise. Java requires that arguments to this operator are expressions, i.e. things which result in concrete values. For example, method call or constant literal are expressions, but a loop or a conditional statement or a variable definition are not expressions because they do not have meaningful value. This means that you cannot return, say, variable definition from a function, but you can return result of method call.
Scala does not have ternary operator. But it does not need one because in Scala everything is expression. Even loops - they result in a special value of Unit type. Conditionals are also expressions, they return a value of common supertype of all branches. Because of this you can use plain conditional "statement" instead of ternary operator. This Scala snippet is equivalent to Java one above:
val x = if (some_condition) 1 else 2
Moreover, in Scala every block is an expression too (its value is the value of the last line in the block), so you can have statements, for example assignments, inside the "ternary operator":
val x = if (some_condition) {
val y = some_computation()
y*2
} else {
val z = another_computation()
z + 3
}

More generally, you can't put arbitrary statements in any expression, not just the conditional expression.
A method's block is a list of statements. A statement is made up of other statements and/or expressions. But expressions, being the building blocks of statements, do normally not contain statements.
Well, there is one exception: a class creation expression of the form
new C(argument list) { class definition }
This creates at compile time a new class that is a subclass of C and at runtime an instance of that class. Hence it would be possible to write something like:
cond ? (new Object() {
public int get() { System.out.println("Hi"); return 42; }
}).get()
: 0
to smuggle a statement into an expression.

The thing you put inside the ?: expression are other expressions. The first one must evaluate to a boolean, and the second and third evaluate to the same type (int, String, etc.). A statement is something that is executed, an expression is something that results in a value. It gets confusing because you can put executable stuff inside an expression.

Related

Exiting the method from a ternary operator

So somewhere in one of my methods, I'm calling another method called foo() and want to use ternary operator to write a short-hand if statement based on what foo()returns. Here's the foo() method:
public boolean foo(String s){
if (!s.contentEquals("")){
System.out.println(s);
return true;
}
return false;
}
The case in which I want to use the ternary operator with the return value of foo() looks like this:
(...)
boolean bool = foo("abc") ? null : return;
(...)
I basically want the method to do nothing and keep going if foo() returns true, and exit out if it returns false.
Is there a way to achieve this using the ternary operator?
The conditional operator ("the operator with three operands" is `a silly way to describe it) is an operator, and all three of its operands need to be expressions.
"return" is not an expression, it is a statement, and therefore can't be used where an expression is needed.
Furthermore, the result of an expression involving the conditional operator is itself an expression, and cannot be used where a statement is needed.
Expressions and statements are different entities in Java.

TernaryOperator (? :) for functions Java. How close we can get? [duplicate]

This question already has answers here:
Java: Ternary with no return. (For method calling)
(6 answers)
Closed 4 years ago.
Java has ternary operator, that works on variables:
(a > 0) ? b++ : c--;
What is a, b, c were functions? I tested something like (a returns boolean, b & c are void):
a() ? b() : c();
but even my Eclipse does not allow that.
How close could I get to this programmatically (to generate functionality to make this with minimum amount of lines)?
I know in other languages like Javascript I could just pass function as parameter, but I have not met similar in Java.
Edit
We have gone in discussion to sidewals because of bad constellation of question, but here is what I want, and it is a real world problem:
What I want to achieve is single-liner to call second or third function based on return value of the first.
With minimum amount of code of course.
What I had tried was that ternary that is made to other things, that I know now.
As others have already explained, the conditional operator is an expression that evaluates to a value, and can thus not be applied to void methods.
If you need a one-liner discarding any return values, use a simple if-else:
if (a()) b(); else c();
If you go through Java Ternary Operator let's you assign a value to a variable based on a boolean expression — either a boolean field, or a statement that evaluates to a boolean result (Its used for assignment). In above you are trying to call functions with return value void that is not allowed in Ternary Operator in java.
The ternary operator, also known as the conditional operator, can be
used as an alternative to the Java if/then/else syntax
You can only use the ternary operator as an expression, not a statement. It's not simply alternative syntax for an if-else - the point is that if a() returns true, the entire expression resolves to the result of b(), else the entire expression resolves to the result of c().
As such, it doesn't make sense if b and c return void. They must return something, and the most specific type that they share will be the resulting type of the whole expression.

When is an unassigned expression a valid statement?

I've read Oracle's expressions tutorial and couldn't understand this.
It is well known that the following line of code is valid Java syntax:
new Object();
However, when I try this with a primitive expression:
(3 + 2);
Eclipse is showing a compile error of "The left-hand side of an assignment must be a variable".
This is true not only for primitives, but also for String literals:
"arbitraryString";
So what is the rule for an unassigned expression to be valid as a Java line of code?
The rule is in the Java Language Specification:
Certain kinds of expressions may be used as statements by following them with semicolons.
ExpressionStatement:
StatementExpression ;
StatementExpression:
Assignment
PreIncrementExpression
PreDecrementExpression
PostIncrementExpression
PostDecrementExpression
MethodInvocation
ClassInstanceCreationExpression
You see that a constructor invocation is a statement. But a String literal or mathematical expression is not.
Creating an object or calling or method can have side effects, I think this is the main reason for this, whereas nothing will ever happen with an arithmetic expression.
Line containing only
new Object();
or to be more precise
new SomeClass();
is acceptable, because code of SomeClass() constructor may be all we want.
But in case of lines containing only
"foo";
or
2;//or (2+3);
compiler knows that beside creating/reusing String literal or integer literal this code doesn't do anything else, which means it is probably some kind of programmer mistake so compiler can't accept it.
You're looking for the difference between expressions and expression-statements. Statements like myVoid(); can be written as a statement: these are void methods, etc. (that's the part you know). Expressions, like (3 + 2); and "arbitraryString", have no side-effects. They can only be treated as a value, as no code is executed. Expression-statements, like new Object(); can have side-effects and execute code, and you sometimes just want this code to be executed and ignore the returned value. The compiler therefore allows this.

Is there any reason for Eclipse showing assignment variables in while loops as valid?

I've been punked numerous times while working on Java in eclipse when I write a while loop like so:
while (recsFinished = true)
When in reality I wanted
while (recsFinished == true)
It's a pretty simple mistake, but it happens to me a lot and it totally throws off the program. And the reason it does is because Eclipse doesn't even throw up a warning when I write the assignment as opposed to the equality equation. This leads me to believe there has to be some reason for a while loop with an assignment equation to exist, but why? I can't think of a single use!
Such assignments are popular in C and C++ (particularly within if statments) and have found themselves part of Java too.
Some folk put the literal on the left hand side: while (true == recsFinished) instead and I'm tempted to suggest that you adopt this programming style only that I personally find it obfuscating. This will issue a compiler error if = is used by accident.
Note well though that comparison to true is redundant. Drop it entirely and use while (recsFinished) instead.
It compiles, since it's valid Java syntax.
It's equivalent to :
recsFinished = true;
while (recsFinished) {
....
recsFinished = true;
}
Which is equivalent to
recsFinished = true;
while (true) {
....
recsFinished = true;
}
which would give you an infinite loop.
It is entirely possible to set a value in the expression of you while loop. If you do this your expression will be evaluated which is alway true because you reset it to true everytime you do a loop.
In most programming languages, the operator = return the assigned value after calling it.
whcih means if you want to assign a variable in every iteration and the value of this variable is the condition then you will use = in the loop condition instead of ==
Example
boolean a, b;
b = true;
while(a = b){
// Some crazy things using a & b
// loop ends when b == false at the end of an iteration
}
To answer your question; It's valid because the specification says so.
You can call a method with an expression,
15.12. Method Invocation Expressions
A method invocation expression is used to invoke a class or instance method.
MethodInvocation:
MethodName ( ArgumentList opt )
ArgumentList:
Expression
ArgumentList , Expression
and an assignment is an expression,
15.26. Assignment Operators
There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.
AssignmentExpression:
ConditionalExpression
Assignment

Replacing an if statement with a disjunction

Just for fun, I was trying to replace:
if (set1.add(x) == false)
{
set2.add(x);
}
with:
set1.add(x) || set2.add(x);
However, Eclipse complains:
Syntax error on token "||", invalid AssignmentOperator
The left-hand side of an assignment must be a variable
Could anybody shine some light onto these error messages? They don't make much sense to me.
As #qqilihq said in the comments try to do
boolean temp = set1.add(x) || set2.add(x);
or more awkward:
if(set1.add(x) || set2.add(x));
According to documentation java statements which can end with a semicolon are:
Assignment expressions
Any use of ++ or --
Method invocations
Object creation expressions
What you've written is not a statement it's an expression. Here you can find more about statements and expressions. So simple but worth to look.
There are a number of answers far, but I agree with Bohemian's answer that the most straightforward simplification (although it doesn't use ||) is this:
if ( !set1.add(x) ) set2.add(x);
That doesn't explain the error message though. Mustafa Genç comes closer on this, but I think it's worthwhile to look at the language specification here. exp1 || exp2 is an expression, and the problem here is that you're trying to use it in a context where a statement is expected. According to 14.8. Expression Statements, some kinds of expressions can be used where statements are expected by attaching a semicolon:
14.8. Expression Statements
Certain kinds of expressions may be used as statements by following
them with semicolons.
ExpressionStatement:
StatementExpression ;
StatementExpression:
Assignment
PreIncrementExpression
PreDecrementExpression
PostIncrementExpression
PostDecrementExpression
MethodInvocation
ClassInstanceCreationExpression
An expression statement is executed by evaluating the expression; if
the expression has a value, the value is discarded.
The reason that you can't do what you're trying to do, though, is that not every expression can be used as a statement. However, it does discuss some ways to work around this. From the same section of the specification (emphasis added):
Unlike C and C++, the Java programming language allows only certain
forms of expressions to be used as expression statements. Note that
the Java programming language does not allow a "cast to void" - void
is not a type - so the traditional C trick of writing an expression
statement such as:
(void)... ; // incorrect!
does not work. On the other hand, the Java
programming language allows all the most useful kinds of expressions
in expressions statements, and it does not require a method invocation
used as an expression statement to invoke a void method, so such a
trick is almost never needed. If a trick is needed, either an
assignment statement (§15.26) or a local variable declaration
statement (§14.4) can be used instead.
This approach is what the first snipped in Reik Val's answer is using:
boolean temp = set1.add(x) || set2.add(x);
I would just:
if (!set1.add(x))
set2.add(x);
The statement
boolean temp = set1.add(x) || set2.add(x);
and any variation thereof is dangerous. You'll hardly ever know what happens there. Note that the right expression is NOT evaludated iff the first expression is true. That is, the attempt to add it to set2 will only be made if it was not yet contained in set1.
EDIT: Now, reading the question again, it seems that this was exactly what you intended. So I think that the anser https://stackoverflow.com/a/21755051 by Mustafa Genç is the relevant here
Usually, you should write clearly what you want to do
boolean wasNotContainedInSet1 = set1.add(x);
boolean wasNotContainedInSet2 = set2.add(x);
boolean wasNotContainedInAnySet =
wasNotContainedInSet1 | wasNotContainedInSet2;
or
boolean wasNotContainedInSet1 = set1.add(x);
if (!wasNotContainedInSet1) {
set2.add(x);
}
or whatever...

Categories

Resources