At the beginning of this course you learned that Java, like the majority of modern programming languages, allows you to group commonly used instructions together into procedures. Rather than “repeating yourself” with duplicated code, you can invoke or call a procedure. Procedures form the basis of procedural programming, providing a layer of indirection that reduces redundancy and make your program much more flexible. Java calls its procedures methods, and every one of them must be defined inside a class. The first method you studied looked like this:
Parameters
Procedures, that is Java methods, gain a large part of their flexibility by allowing you to designate input variables, which are called parameters. When you declare a method you can specify parameters as a comma-separated list of variables, along with their types, between the parentheses that must follow the method name. Each time you call the method, the code invoking the method (the caller) will provide values within parentheses in the same order as the method specifies. Java will copy the values you provide and pass them to the method as arguments. For example you might declare a method indicating int count to determine the number of times to print a welcome message.
You can declare more than one parameter, which include object references as well. And you are not limited to using literal values as arguments. If you have a value stored in a variable, you can use the variable as a method argument as well. Java will copy the value from your variable to the parameter variable when it invokes the method. Thus it is said that Java uses pass by value for method parameters. It is impossible for a method to change the original value of a variable passed as an argument, as the method only receives a copy of that value. This is true regardless of whether the argument and/or parameter values are declared as final. It is still a good idea to use final, of course, to prevent code within each scope from modifying the variables.
If you recall that main(…) is itself a method, it should be obvious that methods can call other methods.
Method Overloading
The name of a method, along with the number and type of its arguments, comprise a method signature. You can have multiple methods with the same name, as long as the signature is different e.g. by providing a different number of parameters. When you provide multiple methods with the same name, it is called method overloading.
Varargs
If you need to pass several values to an array, but you don't know ahead of time how many arguments the caller will want to pass, you can always pass an array. For example, here is a way to print a series of welcome messages if you don't know ahead of time how many messages there are to print.
The problem with this approach is that the caller is forced to create an array of points each time it wishes to call the printWelcome(…) method. In recent versions Java introduced a feature called varargs (for “variable arguments”) which allows you to indicate that the caller may pass some (unknown at compile time) number of arguments when the program is run. Varargs are indicated by adding ... after the type, such as String... instead of String[] in the example above. Behind the scenes, Java will automatically create and pass an array, and the method can treat the argument as it would an array. The benefit is that the caller may simply list the arguments and does not need to manually create an array, as shown below:
Return Values
A method can optionally return a value to the caller by using the return statement followed by the value to return. This makes the method usable in places where an expression is expected. To declare that the method has a return value, simply place the type of value to return (instead of void) in front of the method. For example you could return the total length of all the strings printed in the printWelcome(…) method:
Scope
You already know that in Java a set of braces produces a new scope, and a method is no different. Any variables defined inside a method are stored on the stack, and when the method completes these variables will cease to exist.
But methods present another twist to the idea of scope. When one method calls another, the calling method's scope still exists (although normally inaccessible), waiting for the other method to complete. Once the second method returns, the all the variables defined in the first method are as they were left, and the first method can continue.
Below is an illustration of the call stack that is generated from the example Calling methods from other methods earlier in this lesson, as the main() method calls the printWelcome() method, which in turn calls the printInstructions() method. Read the table from the bottom up. This sequence of saved scopes is referred to as the call stack.
Call stack after three levels of method invocations.
Level
Method
2
printInstructions()
1
printWelcome(…)
0
main(…)
Review
Gotchas
Don't forget to add the keyword static in the method signature. You will learn how to create non-static method in a future lesson.
If your method does not return a value, it must use the word void in place of a return type to indicate this.
In the Real World
Simulate default values in Java by delegating to overloaded methods.
Manually formatting strings by concatenating them is unwieldy and error prone. It is usually preferred to use a formatting utility method such as String.format(String format, Object... args).
Short-Circuiting Returns
If you have a method that has a common control flow but still need to test for certain conditions, it is common to use a “short-circuiting” logic to exit early from a method if the conditions are not met. This is similar to short-circuiting evaluation of Boolean expressions, which you learned about a few lessons earlier. Consider a method that prints welcome to a user, but only if that user has logged in.
That would work just fine. But to make the typical flow of control more explicit, it is common to check for the negative situation and exit the method as a way of short-circuiting the logic. The following might make the intended purpose and common control flow of the method clearer.
Note that “else” is not even needed, because if the first condition holds, program control will have exited, so control passing beyond the condition would only happen if the condition did not hold, which is exactly what else means.
Think About It
Have I written the same or essentially the same code in more than one location? I should consider the Don't Repeat Yourself (DRY) principle and see if I can place the duplicated logic inside a procedure, that is a Java method, perhaps with parameters to make the method more flexible and/or a return value to indicate a result.
Self Evaluation
What is the difference between “pass by value” and “pass by reference”? Which does Java use when passing an object to a method?
Is it possible for a method to change the original value of a variable passed as an argument for a method parameter? Why or why not?
What is the relationship between arrays and varargs?
If a method calls itself, will the new values of the method's variables overwrite the old ones?
Task
In your Booker program you printed information about each of several books. Some day you may wish you reuse this printing procedure elsewhere in your program. You may also find the logic easier to understand if printing logic is grouped separately.
Therefore create a Java method for printing a single book. Then, for each book, call this method, passing it all the information to print that book.