Lesson 12Phase 2: Objects
Methods: parameters and return values
Giving objects behavior, passing values in, getting values back, and how arguments are copied.
By BasithUpdated 60 min
- instance methods
- parameters
- return
- void
- arguments
- pass by value
You can now give an object data: a student with a name and an age, a customer with a balance. But an object that only holds data is half an object. The other half is what it can do. This lesson is about giving objects behavior, the actions that live right next to the data they work on.
- Write a method inside a class: a return type, a name, parameters, and a body.
- Use parameters to pass values into a method, and
returnto pass one back out. - Explain what
voidmeans and when a method should have it. - Predict what happens to a caller's variable when it is passed to a method (pass by value).
- Define two methods with the same name but different parameters: overloading.
new, and to read and set those fields with the dot. You also know variables and types. That is everything you need. We will not touch constructors or inheritance yet; this lesson is only about methods.Behavior belongs with the data#
Here is a Rectangle class with two fields. Suppose we want its area. Width times height. We could compute that out in main, but the area of a rectangle is so obviously a thing a rectangle does that it belongs inside the class, sitting right next to the data it needs.
An action that lives inside a class is called a method. You have already met two: main is a method, and so is the deposit you saw in why objects. Now we write our own. Read this, then press Run.
public class Main { public static void main(String[] args) { Rectangle r = new Rectangle(); r.width = 4; r.height = 3; int a = r.area(); System.out.println("Area is " + a); }} class Rectangle { int width; int height; int area() { return width * height; }}That prints Area is 12. Look closely at the method, because every method you ever write has the same four parts.
The four parts of a method
Reading the line int area() from left to right:
- Return type (
int): the type of value this method hands back to whoever called it. Here it gives back a whole number. - Name (
area): what you call it by. Lowercase first letter, by convention, and usually a verb or a noun that reads like an action. - Parameters (the empty
()): the values the method needs handed in. This one needs nothing extra, so the parentheses are empty. We add some shortly. - Body (the
{ ... }): the code that runs when the method is called.
Key ideaA method is a named action that belongs to a class. It has a return type, a name, a list of parameters, and a body. Every method, including
main, fits this same shape.
def area(self): inside a class and a C function int area(...) do the same job as a Java method. The difference is where it lives: every Java method belongs to a class, glued to the data it works on. Java has no free-standing functions the way C does. Even the void main() of a compact source file belongs to a class that Java supplies for you.Calling a method, and the object it runs on#
You call a method with the dot, the same dot you used to reach a field. r.area()reads as “run the area action belonging to r”. The parentheses are how you say “do it now”.
Now notice something inside the method body. It says width * height, not r.width * r.height. There is no r in sight. How does it know whose width to use? When you call r.area(), the method runs on r, so a bare widthinside the body means “the width of the object I was called on”. The object is already talking about itself.
public class Main { public static void main(String[] args) { Rectangle small = new Rectangle(); small.width = 2; small.height = 2; Rectangle big = new Rectangle(); big.width = 10; big.height = 5; System.out.println("small area: " + small.area()); System.out.println("big area: " + big.area()); }} class Rectangle { int width; int height; int area() { return width * height; }}One method, written once. Called on small it uses small's fields and gives 4. Called on big it uses big's fields and gives 50. The body is shared, but each call works on its own object. This prints small area: 4 then big area: 50.
this, and it matters when names collide. You do not need it yet, because nothing here is ambiguous. We give it a full lesson soon: the this keyword and object references. For now, “a bare field name means my own field” is the right picture.Methods that change the object#
area only reads the fields. A method can also change them. Here is a Counter whose whole job is to hold a number and bump it up by one. The bumping is an action: increment. Press Run, step through, and watch the count field inside the object in the memory view. It is the same object both times; the number changes underneath it.
public class Main { public static void main(String[] args) { Counter c = new Counter(); c.increment(); c.increment(); System.out.println("Count is " + c.count); }} class Counter { int count; void increment() { count = count + 1; }}This prints Count is 2. The field count starts at 0 (an int field that nobody has set is 0, the default you met in creating classes and objects). The first increment() makes it 1, the second makes it 2. The line count = count + 1reads the object's own count, adds one, and stores it back into that same field.
But look at the front of increment. It does not say int, it says void. That is the thing to learn next.
void means “I hand nothing back”
void is the return type you write when a method does its job but has no value to give you. increment changes the counter; there is no number to return. area, by contrast, exists precisely to give you a number, so it returns an int. The return type is your promise about what comes back out.
void.int, double, String, and so on.c.increment();int a = r.area();return; to stop early, or none at all.return a value of the right type on every path out.increment, print, deposit.area, getBalance, toUpperCase.increment is declared void increment(). What happens with the line int a = c.increment();?Parameters: passing values in#
So far increment always adds exactly one. What if we want to add any amount? We let the caller hand a number in. A value handed into a method is passed through a parameter: a variable, declared in the parentheses, that exists only for the length of that one call.
public class Main { public static void main(String[] args) { Counter c = new Counter(); c.add(5); c.add(3); System.out.println("Count is " + c.count); }} class Counter { int count; void add(int amount) { count = count + amount; }}This prints Count is 8. The method now declares one parameter, int amount. When you write c.add(5), the value 5 you put in the parentheses is the argument, and it gets copied into the parameter amount for that call. So the body runs with amount equal to 5, making count 5. The next call copies 3 in, making count 8.
int amount): a named slot waiting for a value. The thing you actually pass at the call is the argument (5): the real value. Parameter is the slot, argument is what goes in it. People mix the words up constantly; you do not have to.A method can take several parameters, separated by commas, each with its own type. The arguments fill them in order, left to right.
public class Main { public static void main(String[] args) { Rectangle r = new Rectangle(); r.setSize(6, 4); System.out.println("Area is " + r.area()); }} class Rectangle { int width; int height; void setSize(int w, int h) { width = w; height = h; } int area() { return width * height; }}This prints Area is 24. The call r.setSize(6, 4) copies 6 into w and 4 into h, by position, and the body stores them into the object's fields. Order matters: setSize(4, 6) would set a different rectangle.
Thermometer class needs a method that takes a temperature in Celsius and hands back the same temperature in Fahrenheit. Which first line fits?return: passing a value back out#
Parameters carry values in. return carries one value out. When a method hits a return, two things happen at once: the method stops right there, and the value after return takes the place of the call. So wherever you write r.area(), the call becomes the number 12.
public class Main { public static void main(String[] args) { Greeter g = new Greeter(); String message = g.greeting("Sana"); System.out.println(message); System.out.println(g.greeting("Ravi")); }} class Greeter { String greeting(String name) { return "Hello, " + name; }}This prints Hello, Sana then Hello, Ravi. The method takes a String parameter and returns a String. The first call's result is stored in message and printed. The second call is dropped straight into println without a variable at all, because a method call that returns a value can be used anywhere that value would be allowed.
“The method stops right there” matters as soon as a method has more than one return. Decide what this prints before you run it.
public class Main { public static void main(String[] args) { Grader g = new Grader(); System.out.println(g.grade(95)); System.out.println(g.grade(72)); System.out.println(g.grade(40)); }} class Grader { String grade(int score) { if (score >= 90) { return "A"; } if (score >= 70) { return "B"; } return "C"; }}A score of 95 passes both tests, score >= 90 and score >= 70. What do the three lines print?
It prints A, B and C, one per line. For 95, the first test is true, so return "A" runs and the method is over: the second test is never even checked. For 72, the first test fails, the second passes, and the method returns "B". Only 40 gets all the way down to return "C". Java also checks that every path through the method ends in a return; delete the last line and javac reports missing return statement.
Key ideaParameters pass values into a method.
returnpasses one value back out, and the call stands in for that value.voidis the special case of passing nothing back.
77.0. A method must work out its result before it can return it.Tap a line, then tap where it should go. Or use the arrows.
Wallet. add puts more coins in the wallet and hands nothing back. canAfford hands back true when the wallet holds at least price coins, and false otherwise. Leave main as it is.Pass by value: Java copies the argument#
Here is a question that trips up almost everyone at first. If I pass a variable into a method, and the method changes its parameter, does my original variable change too? Run this and watch carefully.
public class Main { public static void main(String[] args) { int score = 10; Tweaker t = new Tweaker(); t.tryToChange(score); System.out.println("score is still " + score); }} class Tweaker { void tryToChange(int number) { number = number + 100; System.out.println("inside the method, number is " + number); }}Read the output. Inside the method, number becomes 110. But back in main, scoreis still 10. The method's change did nothing to the caller's variable. The two lines printed are inside the method, number is 110 and then score is still 10.
The reason is the rule Java follows everywhere: pass by value. When you pass score as an argument, Java does not hand the method your variable. It copies the value (10) into the parameter number. From that point on, number is its own separate variable that happens to start at 10. Adding 100 changes the copy. Your original is untouched.
tryToChange is pointless as written. The fix is to return number; and have the caller write score = t.tryToChange(score);. For a value like an int, returning it is how a computed value gets back to where it is needed.80.0, because applyDiscount only changes its own copy of the price. Change the method so it returns the new price, and change main so that price takes the result. The program should print You pay 60.0.The one twist: objects
Pass by value is the whole rule, but there is a subtlety worth a careful sentence. When you pass an objectto a method, the value that gets copied is the reference, the object's address, not the object itself. Both the caller and the method then point at the same object. So the method cannot make your variable point somewhere new, but it canreach through the shared reference and change the object's fields.
public class Main { public static void main(String[] args) { Counter c = new Counter(); Bumper b = new Bumper(); b.bumpTwice(c); System.out.println("count is " + c.count); }} class Counter { int count; void increment() { count = count + 1; }} class Bumper { void bumpTwice(Counter target) { target.increment(); target.increment(); }}This prints count is 2. The Counter we made in main and the target inside bumpTwice are two variables pointing at one and the same counter object, so bumping through target shows up when we read c.count afterwards. It is still pass by value: the reference got copied, and a copy of an address still points at the original object.
Now the other half of that sentence: the method cannot make your variable point somewhere new. This method changes a field through its parameter, then points the parameter at a brand-new counter. Decide what main prints.
public class Main { public static void main(String[] args) { Counter c = new Counter(); c.count = 5; Resetter r = new Resetter(); r.reset(c); System.out.println(c.count); }} class Counter { int count;} class Resetter { void reset(Counter target) { target.count = 0; target = new Counter(); target.count = 99; }}After r.reset(c), what does c.count print?
It prints 0. The first line of reset reaches through the copied address into c's counter and sets its count to 0, and that change sticks. The next line only changes the method's own copy of the address: target now points at a new counter, and c in main knows nothing about it. The 99 goes into that new counter, which nothing points at once the method ends. Step through it and you will see the second Counter appear in the memory view, with c still pointing at the first.
intare copied, so a method cannot change your variable, while objects are shared, so a method can change the object's fields.Overloading: same name, different parameters#
Sometimes one action makes sense in more than one form. A rectangle's area takes a width and a height; a square's area takes only a side. Both are rightly called “area”. Java lets you give two methods the same name as long as their parameter lists differ. This is called overloading, and Java picks the right one by looking at the arguments you pass.
public class Main { public static void main(String[] args) { Areas calc = new Areas(); System.out.println(calc.area(3, 4)); System.out.println(calc.area(5)); }} class Areas { int area(int width, int height) { return width * height; } int area(int side) { return side * side; }}There are two methods named area. The call calc.area(3, 4) has two arguments, so Java chooses the two-parameter version and returns 12. The call calc.area(5) has one argument, so Java chooses the one-parameter version and returns 25. Same name, the argument list decides. The output is 12 then 25. Step through it and watch which area each call jumps into.
Try it
Answer each one in your head or on paper first, then open the answer.
int area(), and say what each one is.void rather than int or String?c.add(5), which word is the parameter and which is the argument?t.tryToChange(score) runs, what does score print, and why?int area(int side) and int area(int width, int height). Which one runs for calc.area(7), and what does it return?Practice
Work through these on your own. Each one builds on this page.
int perimeter() to the Rectangle class (it returns 2 * (width + height)). In main, set a rectangle to width 6 and height 4, then print both its area and its perimeter. Confirm you get 24 and 20.Counter two methods: void reset() that sets count back to 0, and int get() that returns the current count. Make a counter, add some amounts, print get(), then reset(), then print get() again. One method changes the object and returns nothing; the other only reads and returns a value.int max(int a, int b) on a small Math2 class that returns the larger of its two parameters using an if-else. Then overload it: add a second method, also named max, that takes three int parameters and returns the largest of the three. Call both from main and check the results by hand.int parameter cannot change the caller's variable, but a method that takes a Counter parameter can change that counter's fields. Use the words copy and reference.Further reading#
- Defining Methods, the dev.java tutorial on the parts of a method and on overloading
- Calling Methods and Constructors, the dev.java tutorial on parameters and on how primitives and references are passed by value
- Method declarations in the Java Language Specification, section 8.4, the exact rules for parameters, return and overloading
Short guides on this topic
Quick answers to what people search for about it, with code to run.
Spotted a mistake, or a part that did not make sense? Report it by email