It's high time to clear doubts about: The '!' logical-complement operator. With little help of someone who can express himself more clearly, subject should be easier to follow.
Some time ago I used to know all operators priorities in C++, using a minimal number of braces resulted with a quite compact code in logical cases. It was really easy to read that again, but only for me, what I did not see as a problem. When I started to work on the same code with others there were no turn back and I had to make conditions more readable...
Languages and braces
Analysing expression with braces requires to locate them as matching pairs, what takes more effort than a simple sequent reading like I used to do before. But the real breakthrough happened when I had to work on two totally different languages in the same time. Since then, big difference in semantics and operator priorities has changed a lot the way I'm wring code now. If someone has doubts whether a similar situation can happen or not, let's consider quite simple expression like: '2+2*2', what gives '6' in Java and '8' in MUMPS . So finally, I ended using a few braces. When is it essential to use braces?
I suggest doing it in order:
- not to care about operator priorities
- to prevent situation when modification can have impact on current expression meaning
Practise
Notice that adding braces, when it's necessary, might effect in a hard to analyse code, like having 3 and more closing bracket in a row, what's really confusing. Study this example to see how above rules apply:
Let's assume we have some variables:
boolean main = ...;
boolean current = ...;
boolean additional = ...;
if ( main == !current )
if ( main == !current && additional )
if ( main == (!current) )
1.
if ( main == !current && additional )
if ( main == ! current&&additional )
if ( main == (!current) && additional )
if ( main == (! current)&&additional )
if ( main == (!(current && additional)) )
if ( main == (! (current&&additional)) )
So let's see how useful it will be when we exchange 'current' with little wider XML-like expression chain:
data.getAccountData().getUnit().getContract().isOpen()
if (main == data.getAccountData().getUnit().getContract().isOpen() && additional)
if (main == (!data.getAccountData().getUnit().getContract().isOpen()) && additional)
if (main == data.getAccountData().getUnit().getContract().!isOpen() && additional)
instanceof operator
The next example is instanceof operator and it's negation. Now, in order to negate instanceof we must write:
if (!(obj instanceof Person)){...}
if (obj !instanceof Person){...}
Of course those examples require a few analysis, as a behaviour for null-s. But it's not a big deal in this case.
Work around:
We can generate isOpen() as well as isNotOpen() instead of one method in that case. But it is rather a naive solution - isNotOpen() version in most cases calls traditional isOpen() with simple negation, what increases stack trace. Another problem could also occur during a code fixing, because someone could repair only one method (if both would contains implementations).
Hi,
ReplyDeletevery interesting post, but I have a few suggestions.
to: "if ( main == !current && additional )'"
you should write something like that 'if (!current && additional == main), it is more simple because is similar to compiler order interpreting
to: "Work around: We can generate isOpen() as well as isNotOpen() instead of one method in that case."
creating two method isOpen() na isNotOpen() is bad. When we create two method we have to succeses: isOpen() nad !isNotOpen(), and we have two failures: !isOpen and isNotOpen. Now our code is 2 time more complicated and we must remember: the human brain prefers resolve condition at 'true'
@arkadiusz borek
ReplyDeleteI care about compiler as little as possible.
About isOpen/isNotOpen I have exact same opinion as you, although some times it still can be good solution.