welcome back. Now that you've seen a bit about comparisons, let's look at some trickier cases. In this program here, I start out with the number 2. I take its square root. Then I square the root, multiply it by itself. If that gets me back to the original value, I print a message, they're the same. And otherwise, I print what the root squared actually is. Go ahead and run this program and tell me what answer you got.
Okay. When you run this program, you notice that you don't get the answer there the same. But you get that root squared is a value that's close to 2, but not quite. So there's a tiny error in that computation. These errors are unavoidable. Because decimal numbers cannot store an infinite number of digits. And somewhere, some roundoff is going to happen. So when you compare 2 numbers with fractional parts, you're never interested in whether they're exactly the same. You want to know, are they close to each other? Or in other words, is the difference small? Now we want to take the absolute value of the difference. We just care, is that a small positive or negative value? Translated into Java, we compute the absolute value of the difference, and we compare it against a tiny number. In math, one usually calls a tiny number by the name EPSILON. And in many situations, 10 to the minus 12 is a pretty good value to use. Let's do that in our program. Instead of the exact comparison, let's test whether root squared and the original are close to each other. That's the test here. And of course I have to define EPSILON, using the final keyword to indicate that it's a constant. Now when we run the program, we get the expected result. The takeaway is, whenever you compare two decimal numbers, compare whether they're close enough to each other, not whether they're exactly the same.
Perhaps surprisingly, you're going to run into a very similar issue when you compare strings. Check out this code. I have two strings, Uda and city. I concatenated the two together, to form a string that are called firstAndSecond. And over here I have a third string, Udacity. And now I check wether, the first and second which we would expect to be Udacity, is the same as, Udacity. And if so this program prints, they are the same, otherwise it prints what firstAndSecond is. Again, go ahead and run this program yourself and tell me what you find. Just put your output into this box.
Let's compare notes. When I ran this, surprisingly, I did not get the answer, they are the same. Here's what I got. It says, firstAndSecond is Udacity. So why isn't it the same as third, which is also Udacity? Let me explain. FirstAndSecond is a variable. Any object variable contains a reference to the object, like this. The string object contains the letters Udacity. Let's look at our other variable. Again, a variable just holds a reference. Now, reference to which object? Is it a reference to the same string object or to a different one? And that is the key question. The equal equal operator. Checks whether the contents of these two variables is exactly the same. In other words, whether both variables refer the exact same object. Now actually, they don't. Because if you remember what happened, firstAndSecond was obtained by gluing together this string and that string. And a brand new object had to be created to hold that content the third string was initialized with the literal string, Udacity, which came from elsewhere. So, in this case, the objects are different. But we don't actually care about that. What we care about is, do they have the same contents? And to compare the contents, one has to use the equals method. And not the equal, equal operator. Let's do that. Over here, I will check whether firstAndSecond equals third. Here's the test. We call the equals method on firstAndSecond and pass it third as a parameter. Now when we run the program, it will work as expected. The program prints, they are the same. So the take away is, when comparing strings, use the equals method and not the equal equal operator. And as a reminder, when comparing decimal numbers. Be sure to check whether the numbers are close to each other, and also don't use equal equal. Now when do you use equal equal? For integers, of course.
In the previous unit, we've worried about two object references that pointed to different objects. Now we're going to look at a different situation, when object reference doesn't point anywhere at all. Let me explain that. Over here, you have a situation with which you are very familiar. We have a variable that references an object, [INAUDIBLE] cat. Now, my favorite cat is in fact my cat, Eliza. But maybe Sarah doesn't like cats and her favorite wouldn't be any cat at all. That is in the variable her favorite, there wouldn't be any reference to any cat object. In Java, she denote such a reference to no object at all with the keyword null. So this variable here holds this special null reference. This variable here holds a reference to some cat object. Generally, you would use this distinction meaning either a reference to a specific object or a null reference if the object is optional to test whether a variable is null or not. You use equal, equal and you test, is this reference the null reference? Now, if there is a chance that a object contains null. It actually is important that you make that test. Because if you invoke a method on a null reference, then something bad happens and the method called blows up. Well, it doesn't really blow up, of course. But it does terminate your program with a null pointer exception. It is simply illegal to call any method on null, don't do it. Instead, make this test first. I've prepared a little quiz here, where I'm going to ask you about several kinds of tests. So here's the situation, our intrepid reporter went to city hall to get a statement from the mayor. And now I'd like you to give me the java code to test whether the statement was one of the following. Did the mayor say no comment? Just type in here, what you would put inside the parentheses of an if statement to check for that a couple more. Did the mayor say nothing at all? And finally, did the reporter not manage to get a statement? Maybe he was stuck in traffic. So, just type in the conditions for each one of them.
Well, let's compare our answers. Here, we're checking whether the statement equals the string No comment note the use of equals. When you compare two strings, you should use equals. Here, we checked where the mayor said nothing at all. Nothing at all is the empty string, the string that has no contents. And again, we use equals. Alternatively, you could use this test here, where we check whether the length of the statement is 0. That's the same thing as checking whether it equals the empty string. The third test is different. Here, we don't invoke a method. We're checking whether the statement is the null reference. Whether nothing has ever been assigned to the statement.
Remember in lesson three how we stored the friends of a person? We had an instance variable friends, and when we added friends, we added their names to the friends instance variable. To remove friends, we replaced their names, with an empty string. What if I decided to add some methods that comment on the number of friends a person has? And try to use these conditions. Match each condition to a description of its error. The errors are; strings are objects and should be compared using equals. The strings friends will never be null. Loss of precision may prevent this condition from being true even when a person would see that it should be true. And this if statement tries to change the value of the variable instead of comparing it.
The first one is d. The second one is b. The third one is a. And the fourth one is c. Using equals instead of equals equals or .equals is an easy mistake to make. This won't compile. If I create a string friends, and set it equal to the empty string. And now, if friends is the empty screen. And I'm going to use Shift Enter to go down a line without running it. Then I'll print out no friends left. Now if I run this, I get an error incompatible types, because friends equals the empty string, isn't a condition. It can't go inside of an if check. In the second one, friends will never be null. No matter how many friends we remove we just erase their names from the string, we don't reset the string to be null. In this third one, we're comparing using equals, equals. But strings are objects so we always want to use dot equals to compare them. In this last one, we say if the number of friends equals, equals the number of people times 0.33. Then you're friends with one in three people. But these are doubles, and doubles have precision loss. So, you could actually find out that numFriends equals numPeople times 0.33333 or 0.339, or something like that, and this wouldn't be true. Even though that person is basically friends with one in three people. When comparing doubles, you always want to find the difference between them, and then compare it to a threshold. Pick an epsilon, that is the biggest difference you want to accept, and compare the actual difference with your epsilon.
Many times, when you have complicated conditions, you want to combine them. For example, maybe you want to do something if one condition is true, or another one. In Java, there are three operators that you can use to combine conditions. They look like this. The two ampersands are an and these two vertical bars are on or. And the exclamation mark somewhat is a not. You might find it useful to remember that the not equal operator that you've seen a few units ago also has the exclamation mark. Let me show you a few examples for using these operators. And once again, we'll be looking at flags where the visual appearance should help you. Let's start with this maritime flag for the letter d. Here, I made a sketch of this flag. When you're in the middle, the pixel should be blue. And otherwise, it should be yellow. So how do we express when you're in the middle? When the y-value is greater than this level. And less than this level, that's where our and comes in, then we're in the middle. This here is 1 5th of the total height. So, we have two conditions, namely y is at least height over 5, and the other condition is y is at most height times 4 over 5. Both conditions have to be fufilled for us to be in the middle, so we use the and operator. Here it is. Let's just try this out. Here is the condition that you just saw with the and operator here. And in this case, we return blue. Otherwise, we return yellow. In this case, I will make a square flag and here it is. So, the and operator worked out for us. Because we wanted y to be at least here at most, here. Both conditions had to be true for the blue color. Now, let's look at another flag. I've sketched up the flag here. And the question is, when should a pixel be blue. In this case, there are four conditions. The x-value should be at least width over third. That means it's over here. The x-value should be at most, width times two thirds. That makes it over here. The y-value should be the height over 3, and the y-value should be at most the height times two third. All those four conditions need to be fulfilled for a pixel to be built. Now, it's your turn. You'll get to write a program that draws this flag here with a bit of blue on the inside and white on the outside.
Here's how I solve this problem. There are four different conditions. All four had to be true. So I join them with the and operator. So let's start with the x-axis. x has to be at least width over 3, and it should be at most twice that. The y value should be at least height over 3, and it should be at most twice that. So, here are my four conditions joined by and. When all of them are true, then the pixel should be blue, otherwise it should be white. Now, you may be wondering, what's the deal with the greater equals versus less thans, particularly, if you didn't do it just so and your answer wasn't counted correctly. This is easiest to see with a specific example. So let's say we have want each of these three areas to have equal size, so the first one goes from 0 to 99, the next one from 100 to 199, and the last one from 200 to 299. So to be within the second, of these, you want to be greater equal 100 and less than seems like a fine point, but it is worth paying attention to. And here is what it looks like, when I run it.
Now you've seen a couple examples of the use of the hand operator, so how about or? We'll put or to use for drawing this Charlie flag. Again, here is my sketch and we have blue at the top or the bottom. If Y is less than height over 5 or if Y is height times 4 over 5 or larger, then the pixel is blue. So we'll put an or operator here to join these two conditions. Once again, it's your turn to complete this program. Use the or operator for the blue ones, and for the middle strip here, you get to use the and operator one more time. Go for it.
Well, here's how I did it. This is the condition for y's in the top fifth. This is the condition for y's in the bottom fifth. If it's in the top or the bottom, that's where the or comes in, then the pixel is blue. If it's in the middle here, well, that's the case if it's at least at this point, and at most at this point. Notice the and, then it's red, and otherwise it's just white. So now you've gotten good practice with using ands and ors to form complex conditions.
Suppose that x and y are 2 die values. How would you check whether both of them are 3? How would you check whether at least one of them is 3? Write a condition here.
To check whether both of them are 3, you want to check whether the first one is equals 3 and, and y equals, equals 3. To check whether at least one of them is which is the vertical bar twice, y equals, equals 3.
How would you test whether exactly one of them is a 3?
To test whether exactly one of them is a three, you would be interested in two cases. One where x is 3, and y is not. And the other where y is 3, and x is not. If this case is true, or this case is true, then exactly one of them is a
You've been working a lot with and and or now, and the expressions got kind of complicated. In fact, so complicated that they've attracted the interest of mathematicians, such as this stern looking fellow. His name is George Boole. And he figured out the rules for working with conditions that could be true or false. And ever since, that's been called Boolean logic. Now, why do we care? We want our programs to be simpler to read. And so, when we have a long and complex condition, such as this one, we might want to put it in a separate method. Let's think about what this test here was. This was the test for the s flag, where you had a blue square in the middle. And this test you checked, is our pixel, in the middle. Alright, so if we had a method that could test that, we could say, if x and y is in the middle, then we want blue, otherwise white. Much easier to read. Lets go and write this method. Here it is, isInMiddle, takes and x and a y, coordinate. And here, you have the exact same condition that we've had before. And we simply compute, and return that. There's just one catch. We have to specify a return type for this method. And what is this thing that's being returned? Well it's either true or false. In Java, the type, that has two values, true and false, is called, Boolean, in honor, of our friend George. Here it is. So when you have a method, that can return a condition, that's true or false, you declare it as a Boolean method. Then you can use that method inside an if statement just as much as you can use the relational operator. You would want to do that whenever a condition has become so complicated, that you want to put it inside its own method. You can also declare variables of Boolean type. Let me give you a quick example. [SOUND]. I've reimplemented the isInMiddle method to use two Boolean variables. Let's check it out. The first variable, x in middle, checks where the x is between 1 Or to false if it's not. And similarly. This variable over here, yInMiddle, is set to true when this condition is fulfilled, and to false if it's not. Why might I want to do this? Because each of those conditions is complicated enough that by saving it in a variable, it makes the code easier to read. Now over here, I say if both of these conditions are fulfilled then the point is in the middle, so I return the and of these two. Generally, you use a Boolean variable if you want to remember a value that's true of false, so that you can use it later. Sarah has an example of that for you, in a different context.
A while back, the person class had the ability to return the number of friends someone has. We printed the number of friends like this, which works pretty well most of the time. But what if you only have one friend? It would print out, this person has 1 friends. If you want a program to look professional and have output with good grammar, you often need to have the plural form and the singular form of a word. We're going to tackle that problem in a minute. But in preparation, let's write a word class with a method for figuring out if a letter is a consonant or a vowel. Here's the word class. It saves its letters in an instance variable, letters. The is vowel method takes in an index, and then checks to see if the letter at that index is an a. You've probably noticed that this method implementation doesn't really match the description of the method. isVowel should recognize a, e, i, o, and u. Call y a consonant for now. This isConsonant method is completely empty at the moment. It's your turn. Fix the condition in isVowel and implement the isConsonant method. Here's a hint, you shouldn't need to do 21 checks to implement isConsonant.
In is vowel, we want to return true if letters.substring equals a or e or i or o, so we add those comparisons using or. So, it would look like this. I had to type letters dot substring a whole bunch of times maybe I should have made that a variable. And then, every word I used letters.substring i to i plus 1, I could replace them with letter. That looks a lot better now for is consonant a letter is a consonant if its not a vowel. So, I can take a shortcut here and just return not is vowel for the given index. I don't need to tell it that it's an int. And I didn't actually initialize letter. If I want to test my method, I can make a new word. I'll fill it with sleep. And now, if I ask if the first letter is a vowel, I get that it's not because it's an s. If I ask if it's a consonant, I get true. I would probably want to test a couple more cases if I was going to ship this code to somebody. But, this looks like it's working for now.
Now let's implement the method which returns the plural form of a word. We'll make some simplifying assumptions because plurals in English has a lot of rules. Check out the Wikipedia article if you don't believe me, or if you think phoenetics are interesting. We're just going to look at regular plurals, like dish, which becomes dishes. Or cherry, which becomes cherries. Or clock, which becomes clocks. Here are the rules I want you to implement for the initial version. If you want to make a more comprehensive version, nothing's stopping you, but get these ones down first. If the word ends in y, preceded by a consonant, take away the y an add ies, like in cherries. If the word ends in y preceded by a vowel, just add s, like in day which becomes days. If the word ends in o, s, sh, or ch, add es, likes in dishes. Otherwise, just add an s. You can use the is vowel and is consonant methods that you wrote before, as well as another method that I added. The method is takes an index, and the letter to compare it to, and returns true if the letter at that index matches the letter. There's also a tester program with a bunch of examples of correct plural spellings of the words. As a bonus, what happens if you give your program a single letter word, like a. What if you give it an empty string? Does it crash?
So I know that words that end with s or o need to have an es added. So if the last letter is o, or the last letter is s, I return letters plus es. This condition is pretty ugly. And it seems like I'm going to be talking about the last letter index a lot. So I'm going to declare a variable. And now I can replace all of these with just last. That looks a bit better. Let's see if our program works for this case now. I'll need to return something in the else clause. Since just adding an S seemed to be sort of a default leftover case, I'll just return letters plus S here. I'll run my word tester. We want to look at the cases that end with O or S. For example, kisses. It looks like right now, our actual value for kisses matches what we expected. It looks like we also got some for free. All of the leftover default cases where you just add an s, are working pretty well right now. Massages look good, judges looks good, laps, cats, clocks. But there are some that still aren't working, and dishes should be disches, and not dish. So let's go back to our word. The case for y being the last letter was a little bit more complicated, because it mattered whether there was a consonant before it, or a vowel before it. So let's add a case for when the last letter is y. We need to account for two possibilities. If the second to last letter is a consonant, we take off the y and add yes. We can use substring to cut off the last letter, which is the y. And then add back ies. Otherwise, if the letter before the y is a vowel, we just add s. Now it should be better. If I go back to my word tester, and run it one more time. It should work for cherries, which it does. And it hasn't broken. Boys, or days, which also ended in y. So that's a good sign. Still not working well for dishes, or witches. It looks like if it ends in an h, then we need to return the letters with an es instead of just s. Let's check that. It looks like dishes is right, now. Witches is also looking better. Judges, laps, cats, clocks, these all look good. But bathes isn't right now. Bathes just turned into bathes. I think we need an exception for when there's a t before the h. So let's go back and do that. If the last letter is H, then if is the 2nd to last, so if the 2nd to last letter is an S or a C, then we add ES. Otherwise, just S. And now, they should all match. So it looks like it works. But can we simplify the code at all? The code I just wrote was pretty complicated. Now that you have a working version, you could spend 5 minutes trying to simplify the code. You can check whether your simplifications work by running the tester again. And if you get a very simple or very short version, compare it with another short version that I'm going to post in the forum. There should be a link somewhere on the page. [BLANK_AUDIO]
We'll now put together everything that you've learned in this lesson, to solve an important problem, how to validate the input that a human user provided. Think again about the program with which we started this lesson, the elevator simulator. The user enters a number between one and 18, but not 13. There is no button labeled 13. And then that program computed the actual floor. Never mind that part. What I want to talk about is the part where we read the user input. In a program we don't have buttons. But we ask the user to type in the input. There's a prompt. And then what if some crazy user now enters 19 in the hope of getting to the roof or something. Or minus 2. Or some text, users do all sorts of interesting things. And when they do, the program might misbehave. So it's a better idea to catch those first, and just tell the user that, that was not right. Let's list the things that can go wrong and how one can address them in Java. First off, there's no button 13. And so in Java, you might write, if the floor equals 13, then print an error for the user. Let's continue. The next rule is that the bottom floor is one, and the top floor is 18. So if the user enters a number that is less than one, or greater than 18. We want to reject that. Here is the Java code. Note the use of the or operator here. If the floor is less than one, or greater than 18, then we print this error message. And finally, there is the issue. What if the user doesn't even type any number, but some words? That would actually be bad. Because, then, the nextInt method would fail. Let me show you how to protect against that. There is another method that we haven't yet seen, called hasNextInt, which looks at the user input, and, instead of, doing what the nextInt method does, namely returning it It just checks whether it is an integer or not. It returns true, if the user entered an integer. It returns false, if the user entered something else. In other words, it's a method that returns a Boolean value. So we can test for that, in the if statement. If the method returns true, then it is safe to call nextInt. We can read the input, and then process it in any way we want. If on the other hand this method returns false, then we print an error message. Now, it's your turn. Put those three pieces together, and fix up the elevator demo so that any bad input from the user is caught and the program only proceeds when the user did the right thing. An integer between 1 and 18, but not 13.
So here's the original elevator demo, where we read in an integer, or we're hoping it's going to be an integer, and then we use it here for that first demonstration of a new statement. Our task is to make sure that we get a valid integer here. Someone was kind enough to supply us with the bits and pieces that we need. Here is the check where the, the user mistakenly enters 13. Whether the floor is less than 1 or greater than 18. And finally, where that's not an integer at all. Our job is to put these together in the right order. Now, the first 2 checks already assume that the user entered an integer. So we should use the sur check first to test for that. Let's cut it out, and paste it into the main method, and fix up the flow. So, we have our scanner, we're prompting the user to type something in, and now we're checking what the user types. And if that's successful, then we call nextInt. And that gets us to where we were here. So, everything that's down here needs to get inserted here. Let's do that. So, if the user entered an integer, we grab the integer and we do our computation. Let's move on to the other tests. Here they are. Again, I'll cut them out. So, these one's only make sense after I have read in the next input as an integer. So I'll paste them in here. So if the floor is 13, I print an error. If it's less than equal 0 or greater than 18, I print an error. this isn't really going to work because I'm printing the error and then I still continue with all of these things. What I really need to say is if this error happens, print the error, else go on here. Let me do that. So I have enclosed all of the original action inside this else. Now peaking up, there still is a bit of an issue with control flow. The floor is 13, then it does this, and then in all cases, whether or not it's 13, it makes this check. So we also want to put an else in here. So first we check whether the number is even an integer. If so, we check whether it's one of these two special situations. And finally here we know that it is a good input, and we do our regular thing. Note that we have three levels of nesting. This if contains this if, and the else branch here contains another if. C'est la vie.
Some advice that I have for people who are learning how to code for the first time is to not get frustrated or discouraged, especially, if you feel like people are ahead of you or have been coating for so long and your just starting out and there's no way you can catch up. For me, when I first started coding, it was fall quarter freshman year of college and there have been people who've been coding since they were six years old or, you know, in a variety of other classes in their high schools. And I'd never coded before and felt pretty insecure about my, my coding ability. I never had that, that confidence that I could, you know, hack something out really quickly. But I stuck with it, and I think that's what made the difference. I was loving everything that I was learning. I was motivated to go do extra credit or, you know, work on assignments that weren't necessarily due for a certain class I was in. And suddenly, two, three classes in, I felt like I was caught up. I felt like I could find my niche within computer science, where I could be the best. I could be the one who was teaching other people how to do things. And from there, it's been an amazing transformation, in terms of how confident I in my coding ability.