>> Hello, there. Here we are in the Udacity recording studio. >> Last lesson, we used objects from the day and picture classes, and that was pretty useful. But what if I want to make my own classes? >> That's what we're going to be covering today. You will learn how to design, implement, and document your very own classes. >> So then, other people can use my classes in their programs, too. >> That's right, and it's a really important activity and that's why we're covering it early in this course. Let's get going.
Hi, my name is Aina. I'm a junior at Stanford University studying something called Symbolic Systems. It's a combination of computer science, philosophy, psychology, and linguistics. And within that, I'm focusing on human computer interactions. I actually entered Stanford as a pre-vet. I was going to be a human biology major. I started taking the chemistry, biology, and physics classes, and I was getting kind of bored, honestly. So, at the beginning of my sophomore year, I took my first computer science class. I absolutely fell in love with it, and I abandoned all things pre-vet, and it's been a marriage ever since.
Hello and welcome to this new lesson. In the last lesson, you've used objects of classes that other people have created. In this lesson, you will learn how to write your own classes. We'll start out with the class that models a car that drives along. When you design a class, the first thing you ask yourself is. What can one do with the object? Well, imagine a car object. It can drive. Being a car, one will need to add gas once in a while. The object should be able to tell us how many miles it has driven and also how much gas is left. That's all that our car objects will be able to do. Now the first step is to take this English description of the car's capabilities and turn it into the Java code for the public interface. Her is how I like to do it. I first imagine myself being a user of this class, so I have an object, here it's my car and here's how I might want to call the add gas method. The name of the method, addGas and here I'd add 20 gallons. To make the car drive, call the drive method on the car and of course one has to say for how long to drive and here we say 100 miles. And then I might want to find out how much gas is left in the tank after that. So I again use the mycar object, invoke the getgasintank method. It returns the gas level and I store that in a variable. Well, what's the point? I now have more information. I can see that these two methods take arguments and this one doesn't. And I can see that this method here returns a value that's actually a number and these two don't. Now it's fairly easy to write up the publivc interface of the class. I have four methods. Here you see their names. You see that the first two methods have parameter variables and you see that these two methods return values. Let's see more closely on how this process works. Look at the add gas method here. Here, we see it takes an argument, the number of gallons added. I've called that here the amount and it's of type double. This method doesn't return any value, so it's declared as a void. In contrast gas getgasintank has no argument. So there's no parameter variable here but it returns a number. So we put the keyword double here. So the general process when designing a class is to first informally figure out what you want the class to do. Then to put yourselves in the shoe of the user of the class and see how those methods might be called. And then you have enough information to write up the public interface in Java. Sara's going to practice that process with you with another class.
So, we know you're still with us, which of the car methods are mutators? addGas, drive, getMilesDriven, or getGasInTank?
AddGas and drive are mutators. Adding gas changes the state of the car. It now has a more full tank. Drive adds miles, and changes the number of miles driven. Reading the odometer or the gas gauge doesn't change the car. Those methods are accessors.
As professor Horseman develops his car example, we'll do our own more interesting example in parallel. Cars aren't where the money is. The money is in social networks, but existing social networks, I mean my parents are in existing social networks. Maybe we're going to need to start our own new social network. Let's call it Udacious.net. Social networks are all about people, so we're going to need a person class. And the thing we're interested in them doing, is making friends. So, if we have two people, John and Maria, who meet. And now John considers Maria a friend, we need to update our persons objects to reflect that. What might the method call look like in Java?
There are a lot of ways that you could've done this. I chose john.addfriend(mary). We ask the john object to add Mary to John's friends. Now that we know what the method call looks like, we can turn that into an API description. Public void addFriend (Person friend).
As it happens, sometimes those friend relationships don't last. Let's suppose John changes his mind. And for reasons I'll leave to your imagination, wants to unfriend Maria. Can you write the Java API for an unfriend method. And a couple hints, I'm asking for the public interface, not an example method call. And the making a friend public interact from the last question looked like this.
Again there are a few options here, so if yours doesn't look exactly like mine it might still be right. I chose to call the method unfriend, and name the perimeter that comes in nonfriend. This would be the friend that you are unmaking. The method doesn't need to return anything. What we're interested in is that it mutates the object to reflect the new social developments. It's public because it's part of the public interface of the person class. If you got this, good work.
So far, we have two methods in our class. We have add friend, and un-friend. Now, since John and Maria are no longer friends, John went on a friend adding binge. He forgot who was in his friends list because there were so many of them. We need a method to get all the friends in John's friends list. For example, it might return Ann, Fred, Barney. What is the public interface for the method that lists friends? If you need a hint, think about what the method call would look like.
The answer might look like public String getFriends. Maybe you came up with a different names for this, that's totally cool. The method should return the friends in a string. And it's public, because it's part of the public interface of the person class. It doesn't take any arguments. GetFriends is an accessor. Asking about the friends of an object doesn't mutate the object. We're just getting access to the information it can provide. Good work helping me to build the public interface for the person class. We'll come back and figure out how to implement each of these methods later. But for now, back to the professor, who will talk about implementing the car methods.
You've just practiced with Sara how to find the public interface of another class, now let's go back to our car class. We had discovered the public interface that you see over here and now our goal is to implement it. So imagine a car object and let's have a look at the add gas method. We're adding a certain amount of gas and what should that do to the car. Well, remember, it's not a real car and it's not real gas. This car is just an object in a computer, and it remembers some information. In particular, in order to add gas, the car needs to remember how much gas it has. In Java, you use variables to remember anything. And here's is how you write such a variable, a variable has a name just like the variables that you've seen in the previous lesson as well as a type. And then there is this key word private here that simply means that the variables of an object are a part of the private implementation. The programmer who uses the public interface doesn't need to know about that. You will always want to declare instance variables as private. Let me tell you why they're called instance variables. Let's have a look at another car object, or as we say, another instance of the car class. Of course, it also needs to remember how much gas it has. And the amount of gas that this object has will be different from the amount of gas of the other object. So each of these objects, or each of these instances of the car class needs to have its own variable to store that information. That's what it means to be an instance variable. It's a variable that occurs in every instance of the class. Now there's another thing that each car object should remember, namely its mileage. Here's how you can define the variable, we give it a name, miles driven. It's type, it's again a number. And once again, each instance of the car class has its own value, so it needs its own copy of that variable. These are the two instance variables that we need for the car class. You will soon see how the methods of the car class update or read those instance variables. But first, go ahead and practice with Sarah how to discover the instance variables that we need for the person class.
Remember the person class we made an interface for? Now it's time to think about the implementation. What does a person object need to remember for the add friends, unfriend, and get friends methods? There are a lot of possible correct answers here. What do you think?
I think it needs to know the persons name, and the persons friends. If it stores the names of all the friends, it'll be able to give us back that information. A person should also store its own name. We can add other interesting facts to the Person object like where they work, or their favorite restaurants. But for now just to do these methods, we're going to keep it simple. If you feel like customizing your Person class once we've got the basics working, go wild.
Let's think about the person's name first. How will we store the person's name in the person object? How would you declare an instance variable for a person's name?
One answer is private String name. You might have used a different name for the variable here. This declaration says that the variable is private, because it's an instance variable. Its type is String, because it's literal text. And I gave it the name, name, but maybe you came up with something different. As long as it means something like name, that's good.
Great. So far, we have our class for person that has an instance variable for the name. Now we need to store the friends. The string containing his friends will be TJ Jamesha. You'll learn a cleaner way to do this later, but this'll work for now. How would you declare an instance variable for a person's friends?
Did you get private string friends, if so good work. If not, it's okay, you'll get more practice. And if you called this something other than friends, that's fine as long as it means something similar. The next step right now is to learn how the instance variables get their initial values and how they're updated. Let's watch it for the car, and then we'll come back to the Person class.
Alright. Now, you've discovered together with Sarah what instance variables are needed for the person class. So, let's return to our car example and let's start implementing some methods. First off, when you implement a class, you put everything inside a class declaration, which has the keyword class followed by the name of the class, and we generally make all classes public because we want everyone to use them. Inside the class, go the instance variable declarations, as well as the implementations of the methods. So far, I've just put in the method headers here. And we're going to add the method implementations in just a minute. Alright, let's get going. Here is our car class. Here are the instance variables, here is the header of the first method, and we'll put the implementation inside this pair of braces. Now, what do we need to do? Let's say we drive a distance of 100 miles. Then we need to take the miles driven so far. And add the distance to it. That's how the statement looks like. You take the miles driven and add the distance, say 100. Now, miles driven is 100 miles bigger. And we put it back into the variable, so now the variable is updated. Of course, when you drive, you also lower the amount of gas in the take, but we'll come back to that later. Right now, it's your turn, go ahead and implement the addGas method. Simply put in the code for the method inside this text field.
Well when you add gas, you update the gasInTank instance variable. And you simply add in the amount of gas that was added. Here is the code.
Now, we can try this out in BlueJ. Let's make a new car object. Here it is. Let's drive the car for ten miles. Hm, that wasn't very exciting. It would be nice if we could see the car. Let's do that. I'll put a picture in here, and you'll see later how that picture is initialized. And in the drive method, I'll move the picture by the distance trip. Now, let's make another car. You see the picture. Let's drive it, and there it goes. And that was a little bit disappointing, because it only moved by ten pixels. Let's fix that. Let's move it by 10 times the distance, and actually let's do this right. Let's declare a variable, and then use that variable. Now, when the car drives, then it shows it nicely. Okay, now you have seen how to implement some instance methods. And Sarah will walk you through that process with another example, with a person class.
The car's instance methods look pretty good. Now you're going to implement the addFriend method for the person class. We decided earlier that the person class will need instance variables for name and friends. Now the code for add friend will go here. Before you can go this, I need to tell you a couple things about strings and instance variables. You can add strings. If I make a string, addedStrings, which is TJ plus Jamesha, addedStrings now contains TJJamesha. You can add together as many strings as you want. Now to talk about instance variables. A person object can read the name of another person object, friend.name will give you the name of the new friend. There's one more thing. I told you earlier that we should store the names in a string separated by spaces. So I don't want it to look like TJ Jamesha all crammed together I want a space in between each name and the name following it. Now its your turn to implement the addFriend method.
That might have been tricky. The answer should look something like this. There might be other ways to do it, but I think this is the simplest one. If you didn't get this, it's okay. There's more practice coming up. There are some subtletles to how this method works, so let's go through an example. Let's say we have two variables, one for Maria and one for Jamesha. Each of those should contain a person object with a name and friends. We have string objects. For Maria's name, Maria's current friends, Jamesha's name and Jamesha's current friends. Let's say we call maria.addFriend(jamesha). We call this method on the maria object and we pass in as an argument the Jamesha object. Now, when this line runs, the first thing that it does is creates a new string from these ones. This will be the new string that's created. It's going to put together friends, friend.name, and a space. Friends is the friends of the maria object because that's what we called the method on. So, we go to the Maria object, and friends is currently just TJ, with a space. friend.name is the friend, the argument, which was the Jamesha object, dot name, which is Jamesha. And the space is just a space. So, when these strings get put together, it'll look like TJ Jamesha. Now the string created by this part will be assigned into the friends variable. So, the old reference will go away and a new one will be created. And now, Jamesha appears in the friends of Maria. The old friends string that contained just TJ might still be around. And it hasn't changed, but the friends variable in the Maria object now contains a reference to a different string.
The unfriend method removes a name and the space from the friends instance variable. You can use friends.replace to do this. Implement the unfriend method. Remember to remove the extra space. Use friends.replace and figure out exactly how to use it.
The answer is friends equals friends dot replace. Friend name and a space with an empty string. We can look at an example to see how this works. So if this person is Maria and this person is TJ. If we call Maria dot unfriend TJ, we'll come into this method and we'll be doing it for this object. First thing that happens is we call friends dot replace friend dot name with a space after it, and then replace it with an empty string. Friends is initially TJ Jamesha. And friend dot name, whoops, that's actually an error, it should be nonfriend dot name. Do you think that's a compile time error, or a run time error? I'll just fix it. So nonfriend, aas TJ dot name is the string with TJ and nothing else. And then we're adding that together with another space. So we look for the TJ space and friends, and we replace it with an empty string. So this creates a new string, Jamesha with a space. And now we take that string and we make friends, which again was Maria is friends. Stop pointing at TJ Jamesha, and point at just Jamesha with a space.
Sarah has just shown you how to implement some methods of the person class. And here were are back with the car, and ready to implement the remaining methods. Just to remind you, so far, we've implemented two methods. Add gas and drive. And when we drive the car, we can see that it has driven. But we have no way of finding out exactly how much. So let's fix that. We need to add a method that one can call to find out how many miles the car has driven. Here is the method header. And here is the implementation. Note this return statement. That indicates that this method returns a value, namely, the miles driven. Over here, you didn't have a return statement because the method had return type void. It didn't return an answer. The method over here has return type double, and it does return an answer. Any time you implement an accessor, you need to provide a return statement. Alright, now it's you're turn again. Go ahead and implement this method. Just put your code in the box.
Here is how I do it. It's an accessor method so it needs to return something. And what does it need to return? Just a value of the gas in tank instance variable.
Now, we're almost done with implementing the car class, but if you have another look at the drive method, remember, that we didn't update the gas in tank theory variable. That computation is a little bit trickier. Let's first figure it out on a sheet of paper. So, let's say we drive 100 miles. How much gas does that take? Well, of course, that depends on the fuel efficiency. Let's assume we have a car with great mileage. 50 miles per gallon. Well, then the gas consumed is 100 miles divided by the fuel efficiency or 2 gallons. Right, 50 miles for one gallon, 50 miles for another gallon, for a total of the 100 miles. That's something that we can easily program in Java and here's the formula. Let's put that into our code. Of course, that just tells us how much gas was consumed during the drive. We still need to update the instance variable. Let's do that. Here is it. We reduced gas in tank by the amount of gas that was consumed. And we need to set this value here. For now, we'll just use the value of 50 miles per gallon. We'll later see how we can make every car have it's own fuel efficiency. Now, how do we know we did this right? This would be good opportunity for a tester program, and that's what I want you to do next. Write a tester that constructs a new car, fill the car with gas, drive the car. Show the gas level, after the drive. And most importantly, print the expected value. That's what makes a detestor. Go ahead and do that in the coding environment, and then we'll compare notes.
Alright, here's what I did. I filled up the car with 20 gallons. And I drove out the expected value. Well that's the hard part. Let's see what I expect. I drove 100 miles. I assume, 50 miles per gallon. That means, two gallons consumed. I started out with 20 gallons, I consumed two so I should have 18 gallons left over, let's put that in and try it out. Here is the result, the actual gas left is 18 gallons and the expected value is 18 so, everything was working great.
You've just seen how to implement the drive method so that it updates the gas used. And there were two kinds of variables in that method. Let me show you. Over here, you see an instance variable. And inside the body of the drive method you see a different kind of variable called a local variable. What's the difference?
The instance variable, form the long term memory of an object. The kind of thing that an object wants to remember for a very long time. The local variables, on the other hand, are the equivalent of the short term memory. You may remember that you had pizza today but you won't remember it for very long. Whereas you'll always remember your cat. Technically speaking the local variables go away when you reach the end of the method. The instance variables never go away. They stay alive as long as the object is alive. So when do you want to make a variable an instance variable? You want to do that for those values that an object needs to remember throughout multiple methods. You want to make something a local variable when you can forget about it after exiting the method. In this case here we compute the gas consumed for driving this particular distance, update the gasInTank. And then we no longer care that this was the particular amount of gas consumed. We can safely forget it when we exit the method. That's why we put this variable inside the method and made it into a local variable.
Now that you know how to implement methods, let's move on to constructors. You want to be able to create variations of objects. For example, a car that's very fuel efficient or a car that is less so. In Java you use constructors to initialize objects, here is an example. In the body of the constructor we simply initialize the instance variables. The first two here are set to a constant value. When a car is first created, it has not driven any miles and its tank is empty. This instance variable is set to a parameter, and that allows you to construct cars with different properties. Here, we construct a car with 60 miles per gallon. The value 60 is passed into the parameter variable. And is then set the instance variable here. Over here we construct a car with 5 miles per gallon and again, the 5 is passed into the parameter variable. And then used to initialize this instance variable. A couple of things to pay attention to, when you define the constructor, note the name. It's the same as the class name. The class is called car, the constructor is called car. Also note that there's no return type at all not even void. The constructor is not a method. It does not return any value. Instead the job of constructor is just to initialize the instance variables. You can have more than one constructor and you'll get a change to write one right now. Here is our constructor in BlueJ, you see the statements that we had just before, together with a couple of statements that set's the car's picture. Now this constructure always takes the same picture to construct the car. It would be much nicer if we could choose a different picture for every car, so let's do that. I'd like you to write another constructure that takes two arguments, the fuel efficiency and the picture file to be used. Go ahead and do that in the coding environment and submit your answer.
Okay, here's what I do. Since the constructors are pretty similar, I'm going to copy and paste. Now I can keep these statements because I still want to initialize the miles driven as the gas in tank is 0. And I still want the miles per gallon to be the parameter that was passed in. But over here, I don't want this fixed picture. Instead, I want this one, and that's the answer. Now I can construct a current two ways. I can specify the fuel efficiency and get the default picture. Or I can specify both the fuel efficiency and the name of a picture file, and then the second constructor is chosen.
Now that we've added the two constructors, we can see them in BlueJ. I right click on the car class and here they are, pick the second one. Now let's supply both parameters. And here is our car. The other constructor. Only requires one parameter, and we get a car with a default image. Now I'll turn it over to Sarah so you can practice writing constructors for the person class.
Remember the person object? It still needs a constructor too. Here's the code that we had for the person object before, and you know how to make a constructor from the car example. Implement the constructor. It will take a name as an argument, and as a hint, think about what variables do you need to initialize...
To implement the constructor, we need these two lines. We need initial values for both name and friends. So name is initially set to aName, the argument for the constructor, and friends is initially an empty list. Don't worry, it'll fill up as we add friends.
We've got a lot of code for the person class now. We probably want to put it in BlueJ. And make sure that we haven't made any mistakes. So for BlueJ, I'll create a new project, and I'll call it Friends. I need to make a class for the person and then put my code into the class. I'll add the instance variables. The add friend method. And let's just check that it's compiling so far. So far so good. We'll add the unfriend method. Check that it's still compiling. Now I'll go back and put in the constructor. So far so good. Now I should be able to make a person object. And I should be able to add a friend. It's hard to tell whether anything happened yet or not. Maybe we haven't finished yet. Clearly, there's still more to do.
Our person class is looking pretty good, but we're still missing a method. Can you remember what the method was? Maybe you want to pause the video and think about it for a second. Did you pause? The answer is, we forgot the getFriends method. We can put all of this data into our object, but we can't get it back out. John still can't get the list of people he added and forgot. If you remembered that, good work paying close attention. If not, I understand. I forget methods sometimes. You go back and you fix it. In BlueJ, implement the get friends method.
I need to edit the person again and I want to add another instance method. This method is going to return the string with a friend. So let's return type a string and all it has to do is return the value of friends. Now we can go back and if we add Jamesha as a friend to Maria and then we get the friends of Maria. It returns Jamesha with a space just like we expected. I can create an object for TJ as well. Add him as a friend to Maria and then unfriend Jamesha. And now, if I get Maria's friends again, it just contains TJ. Looks like our object is working pretty close to the way we expected. We can now encode a social network using our Java program.
It was pretty cool visualizing the cars. Let's do something like that for the social network. I want to update the constructor for a person object, so that when I create a person, it draws the person's picture. This is how it might look. In person demo, I create three person objects, and I pass in a name of a picture, and also a location. And then when I run all of this, it creates this picture. Can you update the person class so that it will do this when you run the main method in person demo. You will need to take three additional parameters in the constructor: a picture name, an x coordinate, and a y coordinate...
Here is how I did this. I added a string parameter picture name an 'int' parameter 'x' coordinate and an 'int' parameter 'y' coordinate. Now as well as setting name and friends, I create a picture using the picture name and then I translate it to the 'x' and 'y' coordinates and draw it. Good work if you got that.
Now that our program draws the people in the social network, we need to make it draw the friendships. I wrote a main method that creates three people, Sarah, Cheng-Han, and Cay, and then adds Cay and Cheng-Han to my friends. When I run this main method, It draws connections from me to Cheng-Han and me to Cay. This dot means that Cheng-Han is in my friends list, but not necessarily that I'm in his. Your task now is to fix the person class so that when you run this main method, it draws this picture. With the lines. Each person gets a picture when the object's constructed. And when someone adds a friend, you draw a line, from the person who who has a new friend, to the person who is the new friend. The lines for now should just connect all of the top left corners of the pictures. This is a pretty complicated task right now. You might be thinking, you're not really sure what to do first. So let's chat a little. Here's most of our code for the Person class. You're only missing get friends at the bottom. And y-coordinate over here. In which method should we add code that draws a line between friends? The constructor, addFriend, unfriend, getFriends, or somewhere else?
The answer is add friend, though we will need to do some extra work elsewhere to make this happen. Until the add a friend, there's no way to know which line to draw. And we want to draw the line as soon as we know which friends to connect. Which is when the add friend method is called.
Here's another question. When the addFriend method runs, do we have enough information to decide where to draw the circle? Yes, it's in the x coord and y coord variables. Yes, we can get the coordinates from the friend variable. Yes, it's somewhere else. No, we should take the coordinates in as parameters in the addFriend method or no, we should make an instance variable.
The answer is no, we don't have enough information, we should make an instance variable. The xCoord and yCoord variables are parameters in the constructor. Which means when the constructor is done running, they go away. So then when addFriend is called later, xCoord and yCoord don't exist anymore. We can't get the coordinates from the friend variable. We're talking about the coordinates for where to draw the circle. And the friend probably doesn't know anything about where the upper left corner of the current object is. We could take the coordinates in as parameters in the addFriend method, so it would be addFriend(Person friend) int x int y. But it would be a little weird if every time you called addFriend, you also had to pass in the x and y coordinates that you originally passed in in the constructor. So if we decide to make instance variables for x and y, where would we initialize them? That's one for you to think about yourself.
So now imagine that you've added instance variables for x and y. You'll know where to draw the circle when you're adding a friend. And that circle marks one endpoint under the line. But what about the other endpoint? How are you going to get that? Use the instance variables x and y. Use the friend parameter. Use xCoord and yCoord. Get the x and y values from the friend parameter. Or add parameters to the addFriend method to take in the second endpoint of the line.
The answer is, get the x and y values from the friend parameter. Friend is a person object. And we've added x and y instance variables to all of the person objects. The line should join our object to the friend it's adding. We can't just use our own instance variables, x and y. Because then our line would start and end at the same point, and wouldn't join both friends. We can't just use the friend parameter. Because this isn't specific enough. We can't use x coord and y coord. Because those variables aren't accessible anymore. And they contain the first endpoint, not the second endpoint. We could add parameters to the addFriend method to take in the second endpoint of the line. But then every time someone called addFriend, they would have to pass in the coordinates of the friend they're adding. That would make the Person class harder to use.
Now that we've talked through a few parts of this problem, I want you to try to update the person class so that person demo2's main method will work for you the way it does for me. This is tricky, and there are a lot of steps, so take your time. And I'll do the answer one step at a time, so you can watch the first few seconds if you get stuck. Here's one more hint, use the small circle class we give you to draw the circle at the beginning of the line.
I'm going to step through this answer and narrate my thoughts. So, you can pause my answer at any time and go back and try again if you got stuck somewhere. First, I thought about when I want to draw the line. I want to draw it when I add a friend. So, I'm going to need to update the addFriend method. First, I want to draw a circle for the beginning of the line. So, I'll make a small circle and draw it at the top left corner of the picture. But in the addFriend method, I don't have access to that right now. I lost track of the x and y coordinates when the constructor ended. Never saved the top left corner before. Maybe this is something the person needs to remember about it, so I'll create instance variables for x and y. There they are. But I haven't initialized them. Where do I initialize them? I initialize them in the constructor, x starts out as x coord and y starts out as y coord. Now I can draw my circle. It'll just be a new small circle with x and y, and I'll fill it. Now, I want to draw the line. It will start at my upper left coordinate, but where does it end? It ends at the friend I'm adding. If I want to access their instance variables, for x and y, I need to use friend.x and friend.y. So, my line will look like this. Line line is a new line, starts at my , xy coordinates and goes to my friend's x, y coordinates. Last, I need to actually draw the line. And let's see if I did this right. Looks like it compiled and if I run person demo 2, it prints out the picture we wanted. Good work on that quiz. That was really good practice. The next time you see something like this, it'll feel a lot easier than it did this time.
Now we've done a lot of work. We've learned how to identify the instance variables of a class, how to provide the methods, how to write the constructors and the result has been a lot of code. But we've forgotten something very, very important. How does one use this code and who's one? That would be the programmer who doesn't want to know what goes on inside the car class but who just wants to write programs that drive cars around. Now, for a car, when we have an owner's manual but for code, what you want is Javadoc. Let me show you what one needs to put into the code to get the javadoc. Over here you see the same car class that we've been developing. But I've added documentation comments. These comments are enclosed in these delimiters. You see them over here. You see another set of over here. And at the top of the class, you see a class comment that describes the purpose of the class. At the top of every constructor and of every method, you see a method comment. For every parameter there is a line explaining what the parameter means. And for those methods that return a value, there is a comment that explains what the method returns. Now, previously, you have seen the javadoc comments in a webpage, the way to get such a webpage for your own classes is to run the javadoc tool, which we can easily do from within BlueJ. In the main BlueJ menu, pick tools, then project documentation and you'll get to see a webpage in the familiar javadoc format that contains exactly the same comments. They were in the source file that's what javadoc tool does. It takes the comments from the source file and puts them into the web page. For example, here you see the description of a parameter and that's the exact same description that was provided in the source code. Notice that over here this method looks rather naked, no description, no parameter description, oh, that's because there is no java.com in here. So it's your turn now. Go ahead and put it in.
Okay, let's see how one would write such a comment. The method is called addGas. And we need to describe it's purpose in plain English. So, just say Adds gas to the tank of this car. The method has a parameter, and for every parameter, we'll need an @param clause. Put in the name of the parameter, and then a description. The amount of gas to add. Do we need a @return clause? No, because this method, returns void. That's the Java.comment. When you generate the documentation again, now the comment is included in the webpage.
Now you make think it's a bunch of trouble to add all these comments into your code but let me show you something. Here is a part of the documentation of the java string class. When you scroll through it you'll see someone wrote a lot of documentation here, and actually let me tell you who that someone was. You can get the source code for the string class, it's a part of the java development kit. So, here is a part of the actual source code, here is a method and over here. The programmer wrote a pretty long java.comment and not just one. If you scroll through this file, you will see that every method has a comment. Here's another one and you'll see that every method has a comment so if the programmer that give us java can write those comments, so can you.
Let's practice writing documentation. In this version of the Person class, there's a java.comment for getFriends. What's wrong with this documentation comment? The first sentence shouldn't have a period. The first sentence should describe the method. There's no at param statement or the at return statement should describe the method. How would you write this documentation comment?
The problem with this documentation comment, is that the first sentence should describe what the method does. This one just tells us that a person can have friends. That says something about what a person is, but not about the get friends method. We might instead of written something like returns a summary of a person's friends, at return the names of friends, separated by spaces.
Computer science is a highly collaborative study, especially when you're looking at the working world. Everyone works together to pull out and put together this beautiful product. Let's look at Facebook and let's look at Google. You're going to see thousands of programmers sitting in different rooms, coding and sharing their ideas as well as lots of people coming in who want to enhance your idea. Or perhaps change your idea. And you're not always going to be able to be there to explain it. So documenting your code is really, really important to be able to work as a very fast paced efficient team, and to make impact on the products that you work on very quickly.
Now that you've seen a lot of code for implementing methods. Let me show you a technique that can help you understand what a method implementation does. It's called the object tracing technique. So, let's say that you have a, a couple of methods that do some work that may not be so easy to understand. Here's what you do. Pick some scenario, that exercises these methods. Like here, I'm making a car, I add gas, I drive it, and I drive it a bit more. And now we want to understand, what do these methods calls do to the car. It's very low tech. Get yourself an index card or a napkin or some other piece of paper. Make a column for each instance variable. So, let's get started. I put the paper clip so we can see where we are. We construct a car, that initialized the instance variables. I didn't have room to put in the code for the constructor but hopefully you remember. The gas and tank and miles driven are initialized to zero. The miles per gallon with the parameter of the constructor. Let's move on. Now we enter the add gas method here. Amount is thirty. Gas in tank is zero. So, the effective set gas in tank is set to thirty. Moving on. Now we drive ten miles. So, distance is ten. And since this method is a bit longer and it has another local variable here, it makes sense to make another card for it. Or actually, since I ran out of simulated cards, I'll use a napkin. So the distance parameter is 10, over here. We update miles driven, previously it was another local variable. I'll put it on my napkin. It's the distance divided by miles per gallon. 10 divided by 10. So, we consumed a gallon. Now we can look at this statement here. Gas in tank was 30. Gas consumed was 1. The difference is 29. So, that should now by moved into gas in tank. Now, we're at the end of this method call, and the local variable here and the parameter variable go away. So, we can just crumple up our napkin here. These variables are no longer there once we exited this method. Of course, the instance variables, the long-term memory of the object, they stay around. Now here we are on the next method call, we're calling the drive method again. That means that this local variable, and this one here, will get recreated. Of course this time the distance is 20. Now let's see, miles driven is, miles driven plus distance. Previously it was 10. Now we have 20, now it becomes 30. Gas consumed is distance over miles driven, that's 20 by 10 or two. Gas in tank was 29 minus two is 27. So, now we've executed the three steps in this method. This method is now finished. And once again, the local variables go away. And we're done, and this is the remaining state of the object. What's the point? By doing this exercise, you gain an understanding of what the statements in the methods do to the instance variables of an object. Sarah will walk you through an example on how you can use this technique to find a bug in a method implementation.
Let's practice hand tracing with objects. I implemented a get number of friends method, so I can compare how many friends I have with how many friends Chin Han has. Here's the code. Did I get it right? Let's find out by hand tracing these four lines of code. Hand trace it for the Sara variable.
I'm going to trace name, friends and numFriends. I start at the first line, create a new person with the name Sara. So name will be set to the name I passed in. Friends is empty, numFriends is 0. And then we're done with the constructor. We do the same thing for Chenghan. And now we add a friend to Sara. So in addFriend, friends will get longer, it'll be friends plus Chenghan and numFriends will go up by one. And then that method's done, so we go up to the next line. In sara.unfriend, we shorten friends, we replace Chenghan in a space with nothing. So friends goes back to being nothing and then we're done. Something went wrong here, though. I clearly have no friends in the list here. But here, my number of friends is still 1. We saw when that happened. In unfriend, we updated the list of friends, but didn't update the number of friends.
Now Sarah has just shown you how one can use object tracing to find a bug in a method implementation. And I'd like to show you another kind of bug that's fairly common. So over here I have my drive method. I drive a given distance, I update the miles driven. I compute the gas consumed. I update the gasInTank. And when I compile it, I get an error message. Where it says can not find symbol variable gas consumed. It's complaining over here. Now it's a common error to now say oh, it can't find it so let me just make another instance variable. So on the surface it doesn't seem like an error because the class will compile and actually the code will kind of work but it's really inelegant. Let's use the tracing technique to find out why. Note that this class now has four instance variables. Here they are on an index card. And now let's quickly run through the same scenario that we had previously. The car gets constructed, we add 30 gallons, we drive 10 miles. That consumes a gallon. We then drive 20 miles for a total of 30, that consumes 2 gallons. We're left with 27 gallons. So what's wrong? Have a look at this variable. The stuff that it remembers is short-term information. The amount of gas consumed the last time we drove, that's just like remembering what pizza you had for lunch. You don't really need to remember this long term. There's absolutely no need to remember these values after a call to drive, they should be short term memory that should be a local variable. Okay, let's make it so. Over here let's not make gas consumed into an instance variable. Over here we turn it into a local variable simply by adding the type. Remember the difference between an assignment and a variable declaration is nothing but the type. Now the code will compile. And when we review the instance variables up here, we'll find there are no unnecessary one. Your car does need to remember the miles driven at a gas level across multiple method calls. And of course, it needs to know the mileage so that it can compute the gas consumption. So you've just seen how the technique of object tracing gives us an insight into what an object does and it lets us find some errors.
Now, I'd like to talk about a different error. I've on purpose made that error over here. So have a look at the code and tell me what you think is the problem. Is the code so bad that it won't compile? Or will the code run with some errors? Or is the code just inefficient? Maybe gas consumed should be an instance variable? Or is it inefficient in a different way? Maybe miles driven should be a local variable. Okay, have a look, and if you like you can try it out in BlueJ.
Now actually, when you try to compile the code, you'll get an error. The error is somewhat curious. It says that gasInTank might not have been initialized. So, the error here was that gasInTank, by putting the word double here, has turned into a local variable. Not the instance variable that we intended. Let me show you another useful way of thinking about instance variables.
When I get a compiler runtime error, the first thing I do is likely look at the screen and say, darn, it why isn't this right? But I move forward and the second thing I do is tell myself, hey, you can fix this. You get this code, you wrote this, you know where you can find the problem. So, I usually try to comment out certain areas of code or build use cases for it to try to narrow it down. And then, solve the issue within that one part of the code. The third thing I do is I usually get really frustrated by that point, two or three hours later, and I say, I can't do this anymore. Where do I go? So, I try to look at some documentation or I try to look at some tips. And I specifically go and ask some friends for help. The fourth thing that I do is usually I probably solve it by then. So, I give myself a pat on the back and say hey, you can do it. This is awesome. And I learn something, and I write it down. I take a mental note of it so that next time I don't make that same mistake.
When you implement a method, you can make use of a special variable called this that references this very object on which a method is called. Here's what I mean. Look at this method call. It has an argument, number ten, and that argument gets copied into this parameter variable. So now distance is ten, and whenever you use distance, that means ten. Now, when you look at this car, it really has two ingredients, the number ten but also the particular car. And every method in Java has a secret parameter called this that's not actually declare in the method but, in any call, it references the object to the left of the dot. Now, what's the use? If you like, you can add this in front of every instance variable. Let's clean it up a little. So, now it becomes very clear that it's the miles driven of this car that are set to the miles driven of this car plus the distance. And the distance was a parameter variable. It's optional to use this but some people like it a lot because it makes it very clear, which are the instance variables and which ones are local or parameter variables. A bit of terminology by the way, this is sometimes called the implicit parameter because it's never declared. It's implicit in the definition of a method. And a parameter such as this one here is called an explicit parameter. Why don't you give it a try? Here in this drive method, go ahead and add this everywhere that your allowed to do so. How many times did you get to add this and did you like it better this way? No wrong answer, I'd just like to know.
There are five places where you can add the this reference, for the instance variable miles driven, for the instance variable miles per gallon and twice again for the instance variable gas in tank. It is actually pretty nice. Right now, you can clearly tell which ones are the instance variables, and which ones are the local variables. So yeah, I kind of like it. then again, it does make the code longer. That's not so good. I'm obviously conflicted about whether it's a good idea or not. Some people really like it, some people don't like that the code gets longer. Now, Sara will go over with you how the, this reference can be useful in a couple of other cases.
Java code has a special way of saying things like me or my. It's this. We have this reference. The this reference can be used to distinguish between instance variables and parameters. Or local variables, which only belong inside the method. This, no pun intended, solves a problem that we had earlier. Remember in the constructor for the person, we had to give the parameter this odd name, aName. If instead of using aName, we had just used name, then this code would look like this, which won't work. If we name the parameter the same as the instance variable, then this one just assigns the parameter. The value it already has, nothing changes. But you can access the instance variable using this. If you write this.name equals name, then you'll assign the value of name, the parameter variable, into the instance variable. In Java, local variables and parameter variables are considered first when looking up variable names. We say that a local variable shadows an instance variable. The instance variable is hidden in the shadow of the local variable. And to find the instance variable again, you need to use this. This dot name equals name means set the instance variable name to the value of the parameter name.
Now that you've heard of the this reference, open up your code for drawing the lines between people. Remember how we had to make up these special names, xCoord, yCoord and aName, to avoid bugs from shadowing. You wouldn't have called it that at the time but that's what we were doing. I want you to rewrite this constructor to use name, x and y, for the parameter variable names.
Here's how I would fix the constructor. aNme would change to name, xCoord would change to just x, yCoord would change to just y. I have to change aName here, xCoord and yCoord. And now I need to make sure to write this.name equals name, this.x as x and this.y as y. I compiled and got no syntax errors. And when I run the program like I did before, the picture still looks fine.
You can also this to call methods. Here's an example. I created another method, makeMutualFriend, in the Person class. Because a lot of the time when I make a friend with someone else, they also make friends with me. In the method, I add someone else as a friend and then they add me. This is an example of calling one method inside of another method. Here I call addFriend on the current object from inside the makeMutualFriend method. I also call addFriend on the front parameter until it add this object as a friend. In the first method called the impressive parameter Is this object. It's the same as the implicit parameter of makeMutualFriend call. In the second method call the implicit parameter is the friend object. If I remove this dot in front of addFriend here, we would now say that this method call is without an implicit parameter. A method call without an implicit parameter is applied to the same object, so addFriend, friend is the same as this.addFriend, friend. Which way you write it is up to individual preference. I'm using the this reference in other more interesting place here. I'm using it to pass the current object in as a parameter, so when addFriend is called on a friend, It'll know who to add.
Now that I've explained how the makeMutualFriend method works, it's your turn. Can you use the this reference to implement a method mutual unfriend.
Mutualunfriend, will take a parameter. The person this person fell out with, and remove nonfriend from this person's friends list, and remove this person from the nonfriends friends list. This is your last exercise before the summary exercise.
When you look at the world around us, you see many people probably on their cell phones or their laptops, somehow incorporating technology in their lives. I think it's really important for everyone to learn technology or to learn programming specifically because so much of our world is based upon it. Not everyone is going to become a programmer and that's okay. But it's really important for people to understand the basic principles upon which our world is built.