Chapter 4 : Conditionals
Objectives After studying this chapter you should understand the following: the notions of preconditions, postconditions, and invariants; the purpose of a conditional statement; the function of boolean expressions; the purpose of a compound statement. Also, you should be able to: write and evaluate boolean expressions; write conditional statements of various forms; implementation methods that use conditional statements; implement a class tester which automatically verifies test results. May 2004 NH-Chapter4
Postconditions and invariants Method currentCount in Counter is specified as /** * The number of items counted. */ public int currentCount () { … Specification stipulates invoking method returns a value of type int. May 2004 NH-Chapter4
Postconditions and invariants Can be more precise in specifying result : integer result is non- negative. /** * The number of items counted. * * @ensure this.currentCount() >= 0 */ public int currentCount () { … Postcondition: condition that implementor promises will be satisfied when method completes execution. Implementor must make sure that the method satisfies the postcondition. May 2004 NH-Chapter4
Postconditions and invariants Need to make sure instance variable used to store current count will never contain a negative value. Document this via a class invariant: private int count; // current count // invariant: count >= 0 A class invariant will always be true of all instances of the class. May 2004 NH-Chapter4
Postconditions and invariants If we add decrementCount to Counter: public void decrementCount () Decrement positive count by 1; zero count remains zero. In implementation, need to add guard to be sure not to decrement when count == 0 May 2004 NH-Chapter4
The if-then statement if (condition) statement true condition false B E G I N true condition false statement E N D May 2004 NH-Chapter4
Implemeting decrementCount /** * Decrement positive count by 1; * zero count remains zero */ public void decrementCount () { if (count > 0) count = count - 1; } May 2004 NH-Chapter4
Explorer: invariants and guards Restrict Explorer’s tolerance to be a non-negative integer. private int tolerance; // current tolerance // invariant: tolerance >= 0 /** * Damage (hit points) required to defeat * this Explorer. * * @ensure tolerance >= 0 */ public int tolerance () { return tolerance; } Need to insure value assigned in each case is not negative. May 2004 NH-Chapter4
Explorer: invariants and guards Consider method takeThat. Need to guard the two statements: if (hitStrength <= tolerance) tolerance = tolerance - hitStrength; if (hitStrength > tolerance) tolerance = 0; But, only one of those should execute when invoking takeThat. May 2004 NH-Chapter4
if-then-else statement if (condition) statement1 else statement2 B E G I N false true condition statement 2 statement 1 E N D May 2004 NH-Chapter4
Implementing method takeThat public void takeThat (int hitStrength) { if (hitStrength <= tolerance) tolerance = tolerance - hitStrength; else tolerance = 0; } May 2004 NH-Chapter4
Implementing constructor public Explorer (String name, Room location, int strength, int tolerance) { … if (tolerance >= 0) this.tolerance = tolerance; else this.tolerance = 0; } May 2004 NH-Chapter4
Compound statements if (condition) { statement1 … statementN } } else { statement21 statement2M } May 2004 NH-Chapter4
Conditions: boolean expressions Produce boolean values when evaluated; Evaluate to true or false. Can declare boolean variables, and assign values: private boolean tooBig; And can assign values to it: tooBig = true; Or tooBig = size > 10; May 2004 NH-Chapter4
Handling multiple cases A conditional statement divides problem into two cases to be considered independently. In many problems, there are more than two cases, or cases need to be further divided into subcases. Use nesting conditional statements. May 2004 NH-Chapter4
Handling multiple cases Assume class Date with properties day, month,year. Implement a Date’s query that will tell whether or not a date occurs in a leap year. public boolean isLeapYear () This Date occurs in a leap year. May 2004 NH-Chapter4
Handling multiple cases isLeapYear Case: year divisible by 4 ??? Case: year not divisible by 4 not a leap year. Case: divisible by 100 ??? Case: not divisible by 100 is leap Case: divisible by 400 is leap Case: not divisible by 400 is not leap May 2004 NH-Chapter4
Handling multiple cases // This Date occurs in a leap year. public boolean isLeapYear () { boolean aLeapYear; if (year % 4 == 0) if (year % 100 == 0) // if divisible by 100, // must also be divisible by 400 aLeapYear = (year % 400 == 0); else // divisible by 4, not by 100 aLeapYear = true; else // not divisible by 4 aLeapYear = false; return aLeapYear; } May 2004 NH-Chapter4
Cascading conditionals When problem splits into more than two cases: “cascade” if-then-else statements. if (case1) handleCase1 else if (case2) handleCase2 … else if (penultimateCase) handlePenultimateCase else handleLastCase May 2004 NH-Chapter4
TrafficLight’s change() method : case: light is YELLOW change to RED case: light is RED change to GREEN case: light is GREEN change to YELLOW May 2004 NH-Chapter4
TrafficLight’s change() method public void change () { if (light == GREEN) light = YELLOW; else if (light == YELLOW) light = RED; else // light == RED light = GREEN; } May 2004 NH-Chapter4
Dangling else There is an ambiguity as to whether the structure if (condition1) if (condition2) statement1 else statement2 Is it an if-then nested in an if-then-else, or an if-then-else nested in an if-then? May 2004 NH-Chapter4
Dangling else May 2004 NH-Chapter4
Dangling else: equivalent statements if (condition1) if (condition2) statement1 else statement2 if (condition1) { if (condition2) statement1 else statement2 } May 2004 NH-Chapter4
Example: combination lock Responsibilities: Know: The combination whether unlocked or locked Do: lock unlock May 2004 NH-Chapter4
CombinationLock class Class: CombinationLock Queries: is open Commands: lock unlock May 2004 NH-Chapter4
Class CombinationLock specifications public class CombinationLock Contructor: public CombinationLock (int combination) Queries: public boolean isOpen() Commands: public void close () public void open(int combination) May 2004 NH-Chapter4
Class CombinationLock implementation Component variables: private int combination; private boolean isOpen; May 2004 NH-Chapter4
Structure for a simple test public class CombinationLock { private int combination; private boolean isOpen; public CombinationLock (int combination) { } public boolean isOpen () { return true; public void close () { public void open (int combinationToTry) { May 2004 NH-Chapter4
Structure for a simple test class CombinationLockTest { private CombinationLock lock; public CombinationLockTest () { } public void runTest () { public class TestCombinationLock { public static void main (String[] argv) { (new CombinationLockTest()).runTest(); } May 2004 NH-Chapter4
Precondition A condition client of a method must make sure holds when method is invoked. “Require”s : Constructor and method enter have requirements that clients must meet for them to execute properly. May 2004 NH-Chapter4
Constructor specifications /** * Create a lock with the specified combination. * * @require: * 0 <= combination && combination <= 999 * @ensure: * this.isOpen() */ public CombinationLock (int combination) … May 2004 NH-Chapter4
Testing locks Invoke this method in runTest for each lock to test. Need method to create and test lock with given combination: private void testLock (int combination) Test a lock with the specified combination. Invoke this method in runTest for each lock to test. public void runTest () { testLock(0); testLock(123); testLock(999); } May 2004 NH-Chapter4
Automating checking of test results private void verify (boolean test, String message) { if (!test) System.out.println( "Verification failed: " + message); } May 2004 NH-Chapter4
testLock method testLock method must create lock and run initial state test: private void testLock (int combination) { lock = new CombinationLock(combination); testInitialState(); } May 2004 NH-Chapter4
testLock method Initial state test should make sure that lock is open. So, instead of writing the test method: private void testInitialState() { System.out.println("Initial state: " + lock.isOpen()); } Write: private void testInitialState() { verify(lock.isOpen(), "initial state"); } May 2004 NH-Chapter4
Class CombinationLock implementation The straightforward implementations: public CombinationLock (int combination) { this.combination = combination; this.isOpen = true; } public boolean isOpen () { return isOpen; May 2004 NH-Chapter4
Class CombinationLock implementation In constructor, there are two variables with same name. Component variable and local variable combination. this.combination refers to component variable. If variable does not include object reference this in front of it, it is a reference to local variable. May 2004 NH-Chapter4
Class CombinationLock implementation May 2004 NH-Chapter4
Method to test close write method to test close, and invoke it from testLock: private void testClose() { lock.close(); verify(!lock.isOpen(), "close open lock"); verify(!lock.isOpen(), "close closed lock"); } private void testLock (int combination) { lock = new CombinationLock(combination); testInitialState(); testClose(); May 2004 NH-Chapter4
Implementing close in Lock Command close is also easy to implement: public void close () { isOpen = false; } May 2004 NH-Chapter4
Method to test open Test for the method open: four cases to test: closed lock, correct combination: lock opens; open lock, correct combination: lock remains open; closed lock, incorrect combination: lock remains closed. open lock, incorrect combination: lock remains open; Test depends on combination, so pass correct combination as argument: private void testOpen (int combination) May 2004 NH-Chapter4
testOpen method private void testOpen (int combination) { int badCombination = (combination + 1) % 1000; // test with correct combination: lock.close(); lock.open(combination); // open closed lock verify(lock.isOpen(), "open closed lock"); lock.open(combination); // open opened lock verify(lock.isOpen(), "open opened lock"); // test with incorrect combination: lock.open(badCombination); // try opened lock verify(lock.isOpen(), "bad comb, opened lock"); lock.open(badCombination); // try closed lock verify(!lock.isOpen(), "bad comb, closed lock"); } May 2004 NH-Chapter4
testOpen method Add an invocation of this method to testLock: private void testLock (int combination) { lock = new CombinationLock(combination); testInitialState(); testClose(); testOpen(combination); } May 2004 NH-Chapter4
Method open implementation The following implementation of open fails the tests. public void open (int combination) { isOpen = (this.combination == combination) } test will produce the following output: Verification failed: bad comb, opened lock Root of problem: attempt to open an already opened lock with incorrect combination should not close it. May 2004 NH-Chapter4
Method open implementation Correct implementation of method uses a conditional statement that opens the lock if the combination is correct, and does nothing if the combination is not correct: public void open (int combinationToTry) { if (this.combination == combinationToTry) isOpen = true; } May 2004 NH-Chapter4
Digit by digit lock This lock has a 3 digit combination. To open the lock, client provides the digits one at a time. If client enters three digits of combination in order, lock opens. It doesn’t matter how many digits client provides, as long as combination is given. May 2004 NH-Chapter4
Digit by digit lock: combination 123 Digit Entered 4 1 2 3 7 Lock Status closed open May 2004 NH-Chapter4
Digit by digit lock: combination 123 if client gives command close when combination has been partly entered, Client Command Lock Status enter 1 closed enter 2 closed close closed enter 3 ? open enter 1 open enter 2 open enter 3 ? command close resets lock, entire combination must be entered. May 2004 NH-Chapter4
Digit by digit lock If combination is 123 and digits 1-2-2 are entered, we need three digits to open the lock: Digit Entered Lock Status 1 closed (need 2-3 to open) 2 closed (need 3 to open) 2 closed (need 1-2-3 to open) If combination is 223 and the digits 2-2-2 are entered, a 3 will still open the lock: 2 closed (need 2-3 to open) 2 closed (still need 3 to open) May 2004 NH-Chapter4
Class CombinationLock Constructor: public CombinationLock (int combination) require: combination >= 0 && combination <=999 Queries: public boolean isOpen () Commands: public void close () public void enter (int digit) require: digit >=0 && digit <=9 May 2004 NH-Chapter4
CombinationLock responsibilities Know: the 3-digit combination. whether locked or unlocked. the last three digits entered. Do: lock. unlock, when given the proper combination. accept a digit. May 2004 NH-Chapter4
Getting digits from integers. Using % and / operators, can extract each digit. Suppose combination 123. 123 % 10 -> 3 123 / 10 -> 12 12 % 10 -> 2 12 / 10 -> 1 % 10 gets last digit / 10 gets remaining digits. May 2004 NH-Chapter4
CombinationLock implementation private boolean isOpen; // lock is unlocked private int digit1; // 1st combination digit private int digit2; // 2nd combination digit private int digit3; // 3rd combination digit // invariant: // digit1 >= 0 && digit1 <= 9 && // digit2 >= 0 && digit2 <= 9 && // digit3 >= 0 && digit3 <= 9 May 2004 NH-Chapter4
CombinationLock implementation // last, secondToLast, thirdToLast are the last three // digits entered, with last the most recent. // a value of -1 indicates digit has not been entered. private int last; private int secondToLast; private int thirdToLast; // invariant: // -1 <= last <= 9 && // -1 <= secondToLast <= 9 && // -1 <= thirdToLast <= 9 May 2004 NH-Chapter4
Combination is 223, and digits 2-2 have been entered to a closed lock May 2004 NH-Chapter4
Implementing constructor public CombinationLock (int combination) { digit3 = combination % 10; combination = combination / 10; digit2 = combination % 10; digit1 = combination / 10; isOpen = true; last = -1; secondToLast = -1; thirdToLast = -1 } May 2004 NH-Chapter4
Implementing methods Query isOpen is the same as before. public boolean isOpen () { return isOpen; } Command close must resets lock. public void close () { open = false; last = -1; secondToLast = -1; thirdToLast = -1; } May 2004 NH-Chapter4
Implementing method enter public void enter (int digit) { thirdToLast = secondToLast; secondToLast = last; last = digit; if (thirdToLast == digit1 && secondToLast == digit2 && last == digit3) isOpen = true; } May 2004 NH-Chapter4
Relational operators <= less than or equal > greater than >= greater than or equal == equal (A single ‘=‘ denotes assignment.) != not equal A relational expression consists of two expressions joined with a relational operator. May 2004 NH-Chapter4
Relational expressions Let i1=10, i2=-20, i3=30. i1 < 12 true i1 <= 10 true i3 == i1 false i1 != 2 true i1 < 10 false i2 > 0 false 2*i1 == i2+40 true i2*(-1) != i1+i1 false May 2004 NH-Chapter4
Relational expressions An expression like a<b<c is illegal. int values are converted to double, in mixed type comparisons: 10 > 2.5 10.0 > 2.5 false May 2004 NH-Chapter4
Relational expressions floating point values are approximations for real numbers, avoid using equality or inequality operators with them. (1.0/6.0 + 1.0/6.0 + 1.0/6.0 + 1.0/6.0 + 1.0/6.0 + 1.0/6.0) == 1.0 false May 2004 NH-Chapter4
Reference equality and object equality Operators == and != can be used to compare values of any type. Comparing reference values must be of same type. Counter counter1; Counter counter2; Explorer myHero; counter1 == counter2 // legal counter1 == myHero // compilation error May 2004 NH-Chapter4
Reference equality and object equality Two reference values are equal if they refer to same object. How to check if two different objects are equal? if string1, string2, and string3 are String variables, string1 = "c"; string2 = "ab" + string1; string3 = "a" + "bc"; string2 and string3 will reference two distinct objects. string2 == string3 false use String method equals, string2.equals(string3) true May 2004 NH-Chapter4
Boolean operators Use : ! not && and || or ! booleanExpression booleanExpression && booleanExpression booleanExpression || booleanExpression A boolean expression evaluates to true or false May 2004 NH-Chapter4
The ! (not) operator “Not” reverses boolean values. !true false !false true Given : i1=10; !( i1 > 9) !(true) false Beware: “Not” has high precedence. ! i1 > 9 (!i1) > 9 illegal May 2004 NH-Chapter4
&&, || (and then, or else) operators Given: i1=10; (i1>10)||(i1==10) false || true true (i1>10)||(i1<0) false || false false (i1>0)&&(i1<5) true && false false (i1>0)&&(i1<20) true && true true May 2004 NH-Chapter4
&&, || (and then, or else) operators && and || have lower precedence than the relational operators. Parentheses are still useful as they enhance readability. i1 < i2 && i1 < i3 means (i1 < i2) && (i1 < i3) Using the “and then” operator, can express the condition that value of i1 is between 0 and 20: (0 < i1) && (i1 < 20) May 2004 NH-Chapter4
The && and || are lazy operators They evaluate only the left operand only when that suffices. Given i1=10; (i1 < 10) && (i1 > 0) false && (i1 > 0) false (i1 < 11) || (i1 > 100) true || (i1 > 100) true Right operands, (i1 > 0) and (i1 > 100), are not evaluated. Useful when considering expressions like (x == 0) || (y/x < 10) If left operand is true, x is 0, evaluating right operand will result in an attempt to divide by 0 and a run-time error. May 2004 NH-Chapter4
Operator precedence High Low unary +, unary -, ! *, /, % +, - <, <=, >, >= ==, != && || Low May 2004 NH-Chapter4
DeMorgan’s Laws Useful for simplifying expressions. !(b1 && b2) !b1 || !b2 !(b1 || b2) !b1 && !b2 !(i1>5 && i1<8) !(i1>5) || !(i1<8) May 2004 NH-Chapter4
Summary Introduced boolean expressions and conditional statements. Conditional statements allow us to specify alternative computations and the circumstances under which each is to be performed. The particular action to be done is determined by the processor at execution time, depending on the current data values. May 2004 NH-Chapter4
Summary The two forms of conditional statement we’ve seen are if-then and if-then-else. if (condition) statement With an if-then-else, we specify two possible actions, one of which will be done: if (condition) statement1 else statement2 May 2004 NH-Chapter4
Summary Syntax of the Java language permits only a single statement as a conditional alternative. Need a way to package a number of statements into one. Compound statement: put braces around a sequence of statements: { statement1 statement2 … statementn } May 2004 NH-Chapter4
Summary Boolean expression: an expression that evaluates to a boolean value, true or false. Several kinds of operators useful for constructing boolean expressions: relational operators <, <=, >, and >=, equality operators == and !=, boolean operators &&, ||, and !. May 2004 NH-Chapter4
Summary introduced preconditions, postconditions, and invariants. Important in the specification and correctness verification of our designs. Not part of the language per se, we include them in comments. May 2004 NH-Chapter4
Summary Client of a method responsible for making sure that all method preconditions are satisfied before invoking method. Method will work correctly only if preconditions are satisfied. Preconditions are documented in a require clause of method specification. May 2004 NH-Chapter4
Summary Method implementor is responsible for making sure that all postconditions are satisfied when method completes. Postconditions documented in an “ensure clause” of method specification. May 2004 NH-Chapter4
Summary Invariants are conditions that always hold. Class invariants are conditions that are always true of all instances of a class. A class instance is a consistent and coherent model of whatever entity the class represents if class invariants hold. May 2004 NH-Chapter4