Java Methods
Methods in Java
Methods in Java
Now that we’ve covered the basic ideas of adding methods to our programs, let’s see how we can do that using the Java programming language.
We’ve already seen how to create methods in our programs, since each program in Java already includes a method named main
. In general, a method declaration in Java needs a few elements. Let’s start at the simplest case:
public static void foo(){
System.out.println("Foo");
return;
}
Let’s break this example method declaration down to see how it works:
static
at the beginning of this method declaration. That keyword allows us to use this method without creating an object first. We’ll cover how to create and work with objects in a later module. For now, each method we create will need the static
keyword in front of it, just like the main()
method.void
, determines the type of data returned by the method. We use a special keyword void
when the method does not return a value. We’ve already seen this keyword used in our declaration of the main
method.foo
. We can name a method using any valid identifier in Java. In general, method names in Java always start with a lowercase letter.()
that list the parameters for this method. Since there is nothing included in this example, the method foo
does not require any parameters.{}
that surround the code of the method itself. In this case, the method will simply print Foo
to the terminal.return
keyword. Since we aren’t returning a value, we aren’t required to include a return
keyword in the method. However, it is helpful to know that we may use that keyword to exit the method at any time.We’ll cover how to handle method parameters and return values later in this module. For now, we’ll just look at creating simple methods that neither require parameters nor return values.
Once we’ve created a method in our code, we can call, or execute, the method from anywhere in our code using the following syntax:
foo();
We simply use the name of the method, followed by parentheses, wherever we’d like to call that method. Again, we’ll see how to pass arguments to the method and store the return value later in this module.
Let’s look at a complete sample program to see how this all fits together.
public class Methods{
public static void main(String[] args){
System.out.println("Main 1");
foo();
System.out.println("Main 2");
foo();
System.out.println("Main 3");
return;
}
public static void foo(){
System.out.println("Foo 1");
return;
}
}
When we run this program, we should see the following output:
Main 1
Foo 1
Main 2
Foo 1
Main 3
We can also look at a flowchart diagram of this program to help understand how it works:
As we can see in this diagram, the program starts in the main()
method. Inside, it prints Main 1
, then calls the method foo()
. So, we can follow the dashed line over to foo()
, where it will print Foo 1
and return back to main along the same dashed line. Then, we’ll print Main 2
in main()
, before calling foo()
once again. This time, we’ll follow the dotted line to foo()
, where we’ll once again print Foo 1
before returning back to main()
and printing Main 3
.
The line public static void main(String[] args)
is often referred to as a method signature. It contains all the vital information necessary to use the method: its name, what it returns, and what type of parameters it requires. Even public static
inform the programmer on where and how to invoke the method, but we’ll cover these key words when we cover classes.
Methods are a very useful way to divide our code into smaller chunks. However, many times we need to provide input to our methods. This allows us to reuse the same code, but with different values each time we call the method. So, let’s review how to add parameters to our methods in Java.
In Java, we can add parameters to a method declaration by placing them in the parentheses ()
at the end of the declaration. Each parameter is similar to a variable declaration, requiring both a type and an identifier. We can also define multiple parameters, separated by commas ,
.
For example, let’s extend our definition of foo()
from the previous page by adding a String parameter named message
:
static void foo(String message){
System.out.println("Message: " + message);
}
Then, when we call that method, we are required to provide an argument of the correct type. That argument will be stored as the parameter’s variable in foo()
:
foo("Hello World!");
Here’s another example. In this case, we are writing two methods, foo()
and bar()
. They accept multiple parameters, and in main()
we call each method using arguments for each parameter.
public class Parameters{
public static void main(String[] args){
int x = 5;
int y = 10;
int z = 8;
bar(x, y, z);
foo(y, true);
}
static void foo(int output, boolean longMessage){
if(longMessage){
System.out.println("The value was " + output);
}else{
System.out.println("Val: " + output);
}
}
static void bar(int a, int b, int c){
System.out.println(a + ", " + b + ", " + c);
}
}
First, let’s look at bar()
. When we call this method from main()
, we are using x
, y
, and z
as arguments. So, inside of bar()
, the value stored in x
will be stored in a
, y
will be stored in b
, and z
will be stored in c
. The parameters and arguments are matched up based on the order they are provided to the method call. So, bar()
will output 5, 10, 8
when it is called with those parameters.
The call to foo()
is very similar. It only contains two parameters, but each one is a different type. So, when we call that method, we must make sure that the first parameter is an integer, and the second one is a Boolean value.
There are several other things we can do with parameters in our methods, allowing us to use them in new and more flexible ways.
Java allows us to create multiple methods using the same name, or identifier in the same scope, as long as they have different parameter lists. This could include a different number of parameters, different data types for each parameter, or a different ordering of types. The names of the parameters, however, does not matter here. This is called method overloading.
For example, we could create a method named max()
that could take either two or three parameters:
public class Overloading{
public static void main(String[] args){
max(2, 3);
max(3, 4, 5);
}
static void max(int x, int y){
if(x >= y){
System.out.println(x);
}else{
System.out.println(y);
}
}
static void max(int x, int y, int z){
if(x >= y){
if(x >= z){
System.out.println(x);
}else{
System.out.println(z);
}
}else{
if(y >= z){
System.out.println(y);
}else{
System.out.println(z);
}
}
}
}
In this example, we have two methods named max()
, one that requires two parameters, and another that requires three. When Java sees a method call to max()
elsewhere in the code, it will look at the number and types of arguments provided, and use that information to determine which version of max()
it should use.
Of course, we could just use the three argument version of max()
in both cases:
public class Overloading{
public static void main(String[] args){
max(2, 3);
max(3, 4, 5);
}
static void max(int x, int y){
max(x, y, y);
}
static void max(int x, int y, int z){
if(x >= y){
if(x >= z){
System.out.println(x);
}else{
System.out.println(z);
}
}else{
if(y >= z){
System.out.println(y);
}else{
System.out.println(z);
}
}
}
}
In this case, we are calling the three parameter version of max()
from within the two parameter version. In effect, this allows us to define default parameters for methods such as this. If we only provide two arguments, the code will automatically call the three parameter version, filling in the third argument for us.
Finally, Java allows us to define a single parameter that is a variable length parameter. In essence, it will allow us to accept anywhere from 0 to many arguments for that single parameter, which will then be stored in an array. Let’s look at an example:
public class Overloading{
public static void main(String[] args){
max(2, 3);
max(3, 4, 5);
max(5, 6, 7, 8);
max(10, 11, 12, 13, 14, 15, 16);
}
static void max(int ... values){
if(values.length > 0){
int max = values[0];
for(int i : values){
if(i > max){
max = i;
}
}
System.out.println(max);
}
}
}
Here, we have defined a method named max()
that accepts a single variable length parameter. To show a parameter is variable length we use three periods ...
between the type and the variable name. We must respect three rules when creating a variable length parameter:
So, when we run this program, we see that we can call the max()
method with any number of integer arguments, and it will be able to determine the maximum of those values. Inside of the method itself, values
can be treated just like an array of integers.
Lastly, one of the most useful things we can do with methods in our code is return a value from a method. This allows us to use a method to perform an action or calculation that results in a single value that we can use elsewhere in our code. We can even use these method calls just like we use variables in other arithmetic expressions. Let’s take a look at how that works.
To return a value from a method in Java, we use the special keyword return
, followed by an expression representing the value we’d like to return. We must also declare the type of that value in our method declaration, taking the place of the void
keyword we’ve been using up to this point.
Here’s an example program showing how to use the return
keyword and store that returned value in a variable.
public class Return{
public static void main(String[] args){
int returnValue = last(1, 3, 5, 7, 9);
System.out.println(returnValue); // 9
}
static int last(int ... items){
if(items.length > 0){
return items[items.length - 1];
}
return -1;
}
}
Let’s review this program carefully to see what parts of the program are important for returning a value:
void
, we use the keyword int
in the declaration of our last()
method, static int last(int ... items)
. This is because the method must return a value with the type int
.return
keyword. Each instance is followed by a value or expression that results in an integer, which is then returned from the method. As soon as the method reaches a return
keyword, it immediately stops executing and returns that value. So, if the items
variable length parameter is empty, the method will return $-1$. Otherwise, it will return the last item in the items
parameter.main()
method, we see that we’ve included the method call to last()
on the right-hand side of a variable assignment statement. So, once we reach that line of code, the program will call the last()
method and store the returned value in the returnValue
variable in main()
The Java compiler is a very crucial part of making sure that each method we create returns a value correctly. When we compile our code, the compiler checks to make sure that each method that includes a return type other than void
will return a value along all code paths. That means that if one branch of an If-Else statement returns a value, then either the other branch or code below it should also return a value.
In addition, it will make sure that the type of the value returned matches the type that is expected by the method’s declaration.
Finally, just like every other variable assignment in Java, when we store the result of a method call in a variable, Java will also make sure that the variable storing the value has a type that is compatible with the type being returned from the method.
So, if we receive error messages from the Java compiler regarding invalid return types or values in our methods, we’ll need to carefully check our code to make sure we aren’t violating one of those rules.
Let’s try one more example to get some practice building code that contains multiple methods. This program will convert volumes measured in U.S. standard cups into either fluid ounces, tablespoons, or teaspoons. A program that makes these conversions is useful for anyone cooking or baking.
For this example, we’ll need to build a program that matches this problem statement:
Write a program that accepts interactive keyboard input. It should first ask the user to enter a number of cups, and then have the user select the desired conversion from a list of options. The program will then calculate the correct value and display it with the correct units.
The program should contain one class named
Converter
, but may contain several methods.
Please enter the number of cups to convert as a floating-point value: .5
Select conversion: 1 (ounces), 2 (tablespoons) or 3 (teaspoons): 3
24.0 teaspoons
That seems like a pretty straightforward problem statement. Let’s see how we might structure the program.
First, we could look at the problem statement and try to divide the program into a number of methods to perform each action. In this case, it looks like we have a few important actions that could be made into methods:
Based on that breakdown, we can structure the class so it has the following methods:
void main(String[] args)
- this is the usual main method for Java. In this case, we’ll handle input and output in this methodString convert(double cups, int units)
- this method will help us select the proper conversion to be performed based on user inputdouble toOunces(double cups)
- this method will convert the given number of cups to fluid ouncesdouble toTablespoons(double cups)
- this method will convert the given number of cups to tablespoonsdouble toTeaspoons(double cups)
- this method will convert the given number of cups to teaspoonsNow that we have an idea of what methods we need, let’s discuss the overall control flow of the program and the order in which the methods will be used.
The program will start in the main
method, just like any other Java program. That method will prompt the user to input a number of cups to be converted, and also will ask the user to choose which conversion to be performed. Once we have those two inputs, we can then perform the computation in the program.
At that point, the main
method will call the convert
method and provide the two inputs as arguments to that method call. We’ll use the convert
method to determine which of the other methods to call, based on the units
parameter. That method will then call the appropriate conversion method (either toOunces
, toTablespoons
or toTeaspoons
) and then use the value returned by that method to build the output string.
Each conversion method is very simple - it just uses a bit of math to convert the value in cups
to the appropriate value for a different unit of measurement, and then it will return that value.
Now that we’ve decided what methods to include, we can go ahead and start building the overall structure for our program. It should contain a single class named Converter
and the methods listed above. Finally, since we are reading input interactively from the terminal, we’ll also need to remember to import the java.util.Scanner
class. So, our overall structure might look like this:
import java.util.Scanner;
public class Converter{
public static void main(String[] args){
// Create scanner to read input
Scanner scanner = new Scanner(System.in);
// more code here
}
static String convert(double cups, int units) {
// more code here
return "";
}
static double toOunces(double cups){
// more code here
return -1.0;
}
static double toTablespoons(double cups){
// more code here
return -1.0;
}
static double toTeaspoons(double cups){
// more code here
return -1.0;
}
}
Notice that each method signature includes the modifiers public
and static
along with the return type, name of the method, and a list of parameters expected. For every method that returns a value, we’ve also included a default return
statement so that the code will compile at this point. Methods that have void
as a return type, such as the main
method, don’t need to include a return
statement.
Also, the order in which the methods are declared inside of a class does not matter in Java. By convention, the main
method is typically either the first or the last method declared in the class.
Next, we can start filling in the code for the methods. Typically we’d either want to start with the main method, or start with the methods that will be called last in the control flow. In this example, let’s start with the methods that will be called last, which are the conversion methods toOunces
, toTablespoons
, and toTeaspoons
.
We can start with the toOunces
method. A standard cup is 8 fluid ounces. So, our method would include this code:
public static double toOunces(double cups){
return cups * 8.0;
}
That method turns out to be very simple! We can use the same process to write the other two methods. Some helpful conversions:
At this point we’ve written some code, and we may want to test these methods just to make sure they are working before moving on. So, we can write some code in our main
method to quickly call these methods and check their return values. Here’s a quick example:
public static void main(String[] args){
// testing code - DELETE BEFORE SUBMITTING
System.out.println("1 cup should be 8 ounces : " + toOunces(1.0));
System.out.println("1 cup should be 16 tablespoons : " + toTablespoons(1.0));
System.out.println("1 cup should be 48 teaspoons : " + toTeaspoons(1.0));
// Create scanner to read input
Scanner scanner = new Scanner(System.in);
// more code here
}
If we put that code in the main
method and run it, we should see output similar to this:
That’s great! That means our methods are working and seem to be returning the correct values. We may want to try a few other values besides 1 cup just to be sure that the output exactly matches what it should be.
convert
MethodThe convert
method contains the logic for selecting the appropriate conversion method, calling it, and then returning a formatted string to be printed. This method requires two parameters: the cups
value to be sent to the conversion method, and the units
selection from the user that can be used to determine which method to call.
Since the units
item is a mutually-exclusive choice, it makes sense to use an if-else if-else structure in this method:
static String convert(double cups, int units) {
if(units == 1){
return toOunces(cups) + " ounces";
} else if (units == 2){
return toTablespoons(cups) + " tablespoons";
} else if (units == 3){
return toTeaspoons(cups) + " teaspoons";
} else {
// error condition
return "";
}
}
Finally, we can write our main
method. It should prompt the user for the number of cups and the units to be converted to. It will then call the convert
method and print the answer. We should delete our testing code from the main
method at this point.
public static void main(String[] args){
// Create scanner to read input
Scanner scanner = new Scanner(System.in);
System.out.print("Please enter the number of cups to convert as a floating-point value: ");
double cups = Double.parseDouble(scanner.nextLine());
System.out.print("Select conversion: 1 (ounces), 2 (tablespoons) or 3 (teaspoons): ");
int units = Integer.parseInt(scanner.nextLine());
String output = convert(cups, units);
System.out.prinltn(output);
}
With that code in place, we should be able to compile and test our program!
Idea adapted from Gaddis, Tony “Starting out with JAVA”, 5th ed, Pearson: New York 2012 ↩︎