Instance methods define what we can actually do with an object. Everything we covered in ChapterΒ 3Β Methods about writing method still applies. The only new thing about instance methods, compared to the methods weβve written previously, is that instance methods must be called on some specific object because they need to be able to refer to the objectβs instance variables in their code.
This may seem like a small thing but is very powerful because the fact that instance methods can use these variables under the covers allows us to use objects without having to constantly think about how the underlying data is represented.
To take a simple example, consider the method canVote in our Person class from SectionΒ 8.1Β Anatomy of a class. We can call that method on a Person object without having to know exactly what data it uses to answer the question; the method itself gets the data it needs from the object and returns the result.
In this case itβs pretty simple since it only uses one variable. But imagine a more complex Person class that kept track of whether the Person was registered to vote or not? The canVote method might change on the inside but from the outside we would still call it like p.canVote().
Instance methods can be void and non-void. In Person we had one of each: greet is a void method that has the side effect of printing to the screen while canVote is a non-void method that computes and returns a boolean value.
Similarly, instance methods can take any number of arguments. They often will require fewer arguments than static methods because they have access to all the data in the object they are called on.
In this section weβll take a closer look at the things we can do with instance methods and some of the common kinds of instance methods that we will write in many classes. But first letβs make extra sure weβre clear on what it means to call a method on a particular object.
Subsection8.4.1Calling instance methods requires an instance
The reason we have to call an instance method on some particular object is because these methods almost always depend on the values of instance variables in the object. Without an object to provide those values the method wonβt be able to do its job. Calling an instance method without an object is like trying to ask someoneβs name without someone to askβit just doesnβt make sense.
It is possible to write an instance method that doesnβt actually depend on any instance variables. But if it is not declared static it still needs to be called on an instance. There are reasons why we might write such methods to be instance methods but they are beyond the scope of the AP curriculum.
However, there are two pretty different ways we can call a method on a particular object. The normal one is using the same dot operator we used to access instance variables. To access the method via a particular object reference we follow the object reference with a . and then the name of the method followed by the argument list. Most frequently the object reference is stored in a variable so the method call looks like: someVariable.methodName(). Note that thereβs nothing special about a variable as an expression; weβll explore some other options in a moment.
But the other we can call a method is with just the method name: methodName(). Butβand this is an important βbutββthat only works in certain contexts, namely in the code of other instance methods and constructors of the same class. In those contexts, as it does with instance variables, Java will treat a bare name like methodName as a shorthand for this.methodName(), calling the method on the current object.
In other classes and in static methods in the same class, there is no current object so trying to call a method without a specific object reference and the dot operator is back to trying to ask nobody what their name is.
This is why in a classβs main method we must either call only other static methods or construct an instance of the class in order to call instance methods on it.
Finally, thereβs one last way we can fail to have an object to call an instance method on. Remember from SectionΒ 8.2Β Instance variables that trying to access an instance variable with the dot operator on a null reference results in a NullPointerException? The same thing happens if we try to invoke a method on a null reference.
Run the code below to see that it gets a NullPointerException. Fix the code by initializing the variable person in main to a reference an actual Person object.
One simple but common kind of instance methods are getters and setters orβif youβre fancyβ accessors and mutators. These are methods that exist in order to let code outside the class have at least some access to the data stored in the classβs private instance variables. Hereβs an example of both a getter and a setter for the variable age from our Person class.
private int age; // included for reference
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
The pattern is simple: A getter for a variable x is a public method named getX, that takes no arguments, and whose return type is the same as the type of the variable. And a setter is a public method named setX that takes one argument of the same type is the variable and has a void return type. All the getter does is return the value of the variable and all the setter does is assign its argument to the variable.
It might not be immediately obvious what the point of getters and setters are. If we want code outside the class to be able to get and set the value of one of our instance variables, why not just make it public? There are a few advantages:
If we just wrote getAge but didnβt write a setAge then any code could access the age of a Person but only code within the class could change it. Since getters donβt change anything itβs almost impossible for a bug to be caused by someone calling a getter. Setters are much more dangerous.
Even if we do choose to provide a setter, it can still be better than making the variable public because it gives us a place to control how the variable is set. For instance setAge could ensure that the value is positive and generate an error if it is not rather than setting the variable to a nonsense value.
While usually we write getters and setters to provide access to a variable, from outside the class thereβs no way of knowing for sure that there is a variable. This provides a layer of indirection that allows us to change how we represent things inside the class without breaking code in other classes.
For instance, in Person we might realize that we want to store the persons birthdate and to compute their age based on that. We can change the implementation of getAge and then get rid of the age variable and code that uses getAge will still work.
It is quite common to write getters and setters and we should definitely write than rather than making instance variables public. But we shouldnβt write them reflexively. We should think about whether code outside our class really needs to directly access the values of our variables. And we should think even harder about whether want to let outside code change the values of our variables.
Less is more when writing code. We can always add a getter or setter when we discover a true need for it. In the meantime better to keep things as simple as possible.
Try the following code. Note that there are two classes! The main method is in a separate tester or driver class so that it does not have access to the private instance variables in the Student class. (When we write multiple classes in an IDE, we normally put each class in a separate file named after the class it contains. But it is legal to put two classes in one file, as we are here, as long as only one of them is public. Thatβs why Student is not public in this code.)
Try the following code. Note that it has a bug! It tries to access the private instance variable email from outside the class Student. Change The main method in Tester so that it instead uses the appropriate getter to access the value of email.
Try the Student class below which has had some setters added. Notice that there is no setId method even though there is a getId. This is presumably because in the system this class is part of, while it makes sense for a student to change their name or email, their id should never change.
You will need to fix one error. The main method is in a separate class TesterClass and does not have access to the private instance variables in the Student class. Change the main method so that it uses a public setter to change the value instead.
public class Party {
// number of people at the party
private int numOfPeople;
/* Missing header of set method */
{
numOfPeople = people;
}
}
Which of the following method signatures could replace the missing header for the set method in the code above so that the method will work as intended?
Yes, the set method should take a parameter called people and have a void return type. The name of the set method is usually set followed by the full instance variable name, but it does not have to be an exact match.
The parameter of this set method should be called people in order to match the code in the method body. Also the method body does not contain a return statement so the return type must be void.
Another important kind of method is one that returns some kind of representation of the object. In particular thereβs a special method in Java with the signature public String toString().
This method is called automatically in a number of situations when Java needs to convert an arbitrary object to a String. In particular the print and println methods on System.out use it to convert a object argument into a String to be printed. And the String concatenation operators, + and +=, use it to get the String representation of object values.
The reason Java can be sure that it can call toString on any object is because of a feature of Java called inheritance that is no longer covered on AP exam. But the extremely short version is: classes in Java can be related to other classes such that they inherit certain methods from them. All objects in Java inherit methods from the class java.lang.Object, among them toString. So if a class doesnβt contain its own toString, it will have the one that it inherits from Object. Unfortunately the strings it returns are not all that useful for human beings. For instance our Person classβs toString, inherited from Object, returns strings like "Person@6d4b1c02". If a class contains a toString method, that method overrides the inherited one, allowing us to return a more useful String representation.
Here is the Student class again, but this time with a toString method. Note that when we call System.out.println(s1) it will automatically call the toString method to get a String representation of the Student object. The toString method will return a String that is then printed out. Watch how the control moves to the toString method and then comes back to main in the Java visualizer by using the Show CodeLens button.
Subsection8.4.4Methods with and without side effects
In our earlier discussions of methods, we drew a distinction between methods that compute and return values (non-void methods) and methods that are called for their side effects, which usually have a void return type to make it clear that they are called for the effect they have on the world, such as printing output.
Instance methods can fall into both categories. For example, in Person the canVote method computes a boolean value but doesnβt have any side effects. And greet doesnβt return anything but does print some output.
But thereβs another way instance methods can have side effects that we havenβt had to deal with before: they can change the value of the objectβs instance variables. This is a side effect because the effect of the change is visible after the method has returned. In actual programs this kind of side effect is much more common than things like printing or drawing on the screen.
Side effects can be very powerful but they can also make programs harder to understand. Changing the value of an instance variable in a way that changes the behavior of methods on that object means that understanding those methods requires us to understand not just what the method does but how what it does can be affected by changes to the object. And then to understand a whole program we may have to trace all the ways an object can change state between method calls.
Which is not to say we should never write methods with side effects. Ultimately our programs have to have some effect or whatβs the point of running them. But it is a good idea to be clear about whether the point of a method is to compute a value, based on some combination of its arguments and the current values of the objectβs instance variables, or to have a side effect, either a human visible effect or the side effect of changing the objectβs state.
Methods that do both are occasionally useful, but they usually take the form of methods that update a variable and then return its new value as a convenience. For example this method is a non-void method that updates a counter variable and then returns the new value, presumably because the code that calls this method will frequently want to know that value.
public int updateCounter() {
counter++;
return counter;
}
Also note that non-void methods can do a lot more interesting things than just return the value of a variable. Some methods take arguments and return values that are computed using those arguments and the objectβs instance variables.
Run the following program which contains a method letterPresent that takes a letter argument and uses a loop to check if the letter is in the text and returning true if it is and false otherwise.
Then change the call to letterPresent in main and run it again to try finding a different letter. Try one that is not present in the string "Apples and Oranges". Or try passing a differest String to the constructor call in main.
After running the code, make the tests pass by adding a new method letterCount that works in a similar way but instead keeps track in a variable called count how many times the letter occurs in the text and returns it. What will the return type need to be?
As with constructors, we can overload methods. While we normally describe it as overloading a method, what weβre really overloading is the name, writing multiple methods with the same name but different parameter lists. Thus the one name is overloaded by bearing the weight of more than one method.
The key thing to understand about overloading methods is that it doesnβt actually let us do anything that we couldnβt do using methods with different names; when Java sees code that calls a method it uses the name and the types of the arguments to determine what method to call.
If we took some code containing overloaded methods and renamed them all to have unique names, and then changed all the code that called those methods to use the new names, it would work exactly the same.
While the return type of a method is sometimes considered part of the method signature, it is not for purposes of overloading. That is, Java will not let us write two methods with the same name and parameter lists that differ only in their return type. Typically overloaded methods with the same name will have the same return type except in cases like Math.abs where each overloaded method returns the same type as it accepts as an argument.
Overloading just gives us a way to indicate that two methods do βthe same thingβ with slightly different arguments. Itβs up to us what βthe same thingβ means but typically the reasons for writing overloaded methods are similar to the reasons for writing overloaded constructors: to make versions of a method that provide useful default values for certain arguments or to accept different types of arguments, either because one type can meaningfully be converted to another or because we want to do βthe same thingβ but with two different types, like Math.abs does.
public class Circle {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getArea() {
return 3.14159 * radius * radius;
}
public static void main(String[] args) {
Circle c = new Circle(10);
System.out.println(c.getArea());
}
}
public class Rectangle {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public void resize(int amt) {
width += amt;
}
public int getArea() {
return width * height;
}
public static void main(String[] args) {
Rectangle r = new Rectangle(10, 15);
r.resize(5);
System.out.println(r.getArea());
}
}
Given the Rectangle class below, use its constructor to create a 5x10 rectangle, then call its resize method to add 10 to its width, and then print out its area using its getArea method.
public class MethodTrace {
public int square(int x) {
return x * x;
}
public int divide(int x, int y) {
return x / y;
}
public static void main(String[] args) {
MethodTrace traceObj = new MethodTrace();
System.out.println(traceObj.square(2) + traceObj.divide(6, 2));
}
}
The Turtle class has a method called getDistance(x,y) which will return the turtleβs distance from a point (x,y). Can you find yertleβs distance from the point (0,0)? In the exercise below, add another turtle and make both turtles move. Then find the distance between them. You must use the getXPos and getYPos methods as well as the getDistance method.
Subsection8.4.7Coding Challenge: Virtual Pet Class
In the late 1990s and early 2000s, digital pets like in the photo were a huge hit! You had to feed, play, and interact with your virtual pet in order to keep it healthy and happy. Letβs think about how they were programmed.
Design: In pairs, brainstorm about the object-oriented design for the virtual pet. What data or instance variables would you need to keep track of for a virtual pet? What behaviors or methods would the virtual pet have? (You could draw a Class Diagram for this class using app.diagrams.net or Creately.com, although it is not required). Then, using the Person class above as a guide, write a VirtualPet class in the Active Code template below with the following parts.
Instance Variables: Declare at least 3 instance variables in the VirtualPet class below. Two of the instance variables should be called name and health where health is a number from 0 to 10. The rest can be variables from your design above. Donβt forget to add in their private access modifiers.
Methods: Complete the print method to print out the instance variables of the VirtualPet object, and complete the feed method to add 1 to the health instance variable. Remember that the methods have direct access to the instance variables. Create at least one more method that changes one of the instance variables from your design. (The constructor method and a get method is written for you below. You will learn how to write constructors in detail in the next lesson.)
Complete the main method to construct at least 2 VirtualPet objects that call the VirtualPet constructor given to you with arguments for name and health. Then, use at least one of the objects to call its feed and print methods.
Complete the VirtualPet class below. Add at least 3 instance variables, a print method, a feed method, and at least 1 other method that changes an instance variable. Then complete the main method to construct at least 2 VirtualPet objects, call the feed and print method with one of them.
Youβve been hired to create a software system for the Awesome Animal Clinic! They would like to keep track of their animal patients. Here are some attributes of the pets that they would like to track:
Create a class that keeps track of the attributes above for pet records at the animal clinic. Decide what instance variables are needed and their data types. Make sure you use int, double, and String data types. Make the instance variables private.
In the main method below, create 2 Pet objects with different values and call the constructor, accessor methods, mutator methods, and toString methods to test all your code.
Create a Pet class that keeps track of the name, age, weight, type of animal, and breed for records at an animal clinic. Create a constructor, getter, setter, and toString() methods. Create 2 Pet objects in the main method and test all your methods.
Write an additional method for your class that takes a parameter. For example, there could be a print method with arguments that indicate how you want to print out the information, e.g. print(format) could print the data according to an argument that is βplainβ or βtableβ where the data is printed in a table drawn with dashes and lines (|). Or come up with another creative method for your class.
Copy your class from lesson 3.4. Add get, set, toString, and a method that takes a parameter. For example, there could be a print method with arguments that indicate how you want to print out the information, print(format) where format is βplainβ or βtableβ.
(AP 3.5.A.3) In non-void methods, a return expression compatible with the return type is evaluated, and the value is returned. This is referred to as return by value.
(AP 3.5.A.4) The return keyword is used to return the flow of control to the point where the method or constructor was called. Any code that is sequentially after a return statement will never be executed. Executing a return statement inside a selection or iteration statement will halt the statement and exit the method or constructor.
(AP 3.5.A.5) An accessor method (getter) allows objects of other classes to obtain a copy of the value of instance variables or class variables. An accessor method is a non-void method.
(AP 3.5.A.6) A mutator (modifier) method (setter) is a method that changes the values of the instance variables or class variables. A mutator method is often a void method.
(AP 3.5.A.8) When an argument is a primitive value, the parameter is initialized with a copy of that value. Changes to the parameter have no effect on the corresponding argument.
The toString method is an overridden method that is included in classes to provide a description of a specific object. It generally includes what values are stored in the instance data of the object. If System.out.print or System.out.println is passed an object, that objectβs toString method is called, and the returned String is printed. An objectβs toString method is also used to get the String representation when concatenating the object to a String with the + operator.
A method signature is the method name followed by the parameter list which gives the type and name for each parameter. Note that methods do not have to take any parameters, but you still need the parentheses after the method name.
A method call interrupts the sequential execution of statements, causing the program to first execute the statements in the method or constructor before continuing. Once the last statement in the method or constructor has executed or a return statement is executed, the flow of control is returned to the point immediately following the method or constructor call.
Non-void methods are methods that return values. You should do something with the return value, such as assigning it to a variable, using it in an expression, or printing it.
Consider the following Party class. The getNumOfPeople method is intended to allow methods in other classes to access a Party objectβs numOfPeople instance variable value; however, it does not work as intended. Which of the following best explains why the getNumOfPeople method does NOT work as intended?
In the Party class below, the addPeople method is intended to increase the value of the instance variable numOfPeople by the value of the parameter additionalPeople. The method does not work as intended.
public class Party {
private int numOfPeople;
public Party(int n) {
numOfPeople = n;
}
public int addPeople(int additionalPeople) // Line 10
{
numOfPeople += additionalPeople; // Line 12
}
}
Which of the following changes should be made so that the class definition compiles without error and the method addPeople works as intended?
public class Party {
private int numInvited;
private boolean partyCancelled;
public Party() {
numInvited = 1;
partyCancelled = false;
}
public void inviteFriend() {
numInvited++;
}
public void cancelParty() {
partyCancelled = true;
}
}
Assume that a Party object called myParty has been properly declared and initialized in a class other than Party. Which of the following statements are valid?
public class Cat {
public void meow() {
System.out.print("Meow ");
}
public void purr() {
System.out.print("purr");
}
public void welcomeHome() {
purr();
meow();
}
/* Constructors not shown */
}
Which of the following code segments, if located in a method in a class other than Cat, will cause the message βMeow purrβ to be printed?
Correct, the Liquid() constructor sets the currentTemp instance variable to 50, and the lowerTemp() method subtracts 10 from it, and getTemp() returns the currentTemp value as a double.
public void splitPizza(int numOfPeople) {
int slicesPerPerson = 8/numOfPeople;
/* INSERT CODE HERE */
}
public void printSlices(int slices) {
System.out.println("Each person gets " + slices + " slices each");
}
Which of the following lines would go into /* INSERT CODE HERE */ in the method splitPizza in order to call the printSlices method to print the number of slices per person correctly?