Methods
Note
A fact about methods: Almost all our code will be inside a method!
Clearly, methods are important. While this book will typically focus on the practical- getting-things-done aspect of programming it is also important to cover the necessary theory as well so that we can make fast progress and end up with a full understanding at the end.
Having said this, it is not necessary to master or memorize everything about method theory before moving on with the project. If something doesn't quite make sense, the most likely reason is that something else later in the book will make things come more in to focus.
Tip
Thoroughly read everything about methods but don't wait until you are 100% confident with everything in this section before moving on. The best way to master methods is to go ahead and use them.
Methods revisited and explained further
As a refresher, this image roughly sums up where our understanding of methods is now. The ticks indicate where we have discussed an aspect relating to methods and the crosses X indicate where we have not explored yet.
As we can see in the previous image, there are many more crosses than ticks around methods. We will totally take the lid off the methods and see how they work, and what exactly the other parts of the method are doing for us later in the chapter. In Chapter 8, Object-Oriented Programming, we will clear up the last few parts of the mystery of methods.
So, what exactly are Java methods? A method is a collection of variables, expressions and other code bundled together inside an opening curly brace {
and closing curly brace }
preceded by a name and some more method syntax too. We have already been using lots of methods, but we just haven't looked very closely at them yet.
Let's start with the signature of methods.
The method signature
The first part of a method that we write is called the signature. And as we will see soon the signature can be further broken down into other parts. Here is a hypothetical method signature.
public boolean shootAlien(int x, int y, float velocity)
If we add an opening and closing pair of curly braces {}
with some code that the method performs then we have a complete method - a definition. Here is another hypothetical, yet syntactically correct method.
private void setCoordinates(int x, int y){ // code to set coordinates goes here }
We could then use our new method from another part of our code like this:
… // I like it here // but now I am going off to setCoordinates method setCoordinates(4,6); // Phew, I'm back again - code continues here …
At the point where we call setCoordinates
, the execution of our program would branch to the code contained within that method. The method would execute all the statements inside it, step by step until it reaches the end and returns control to the code that called it, or sooner if it hits a return
statement. Then the code would continue running from the first line after the method call.
Here is another example of a method complete with the code to make the method return to the code that called it.
int addAToB(int a, int b){ int answer = a + b; return answer; }
The call to use the above method could look like this:
int myAnswer = addAToB(2, 4);
We don't need to write methods to add two int
variables together, but the example helps us see a little more into the workings of methods. This is what is happening step by step:
- First, we pass in the values
2
and4
. - In the method
signature,
the value 2 is assigned toint a
and the value 4 is assigned toint b
. - Within the method body, the variables
a
andb
are added together and used to initialize the new variableint answer
.
The line return answer
returns the value stored in answer
to the calling code, causing myAnswer
to be initialized with the value 6.
Look back at all the hypothetical method examples and notice that each of the method signatures varies a little. The reason for this is the Java method signature is quite flexible allowing us to build exactly the methods we need.
Exactly how the method signature defines how the method must be called and how the method must return a value, deserves further discussion.
Let's give each part of the signature a name so we can break it into chunks and learn about them.
Here is a method signature with its parts labeled up ready for discussion. Also, have a look at the below table to further identify which part of the signature is which. This will make the rest of our discussions on methods straightforward. Look at the next image which is the same method as the one in the previous image but this time I have labeled all the parts.
Note in the image I have not labeled the @override
part. We already know that this method is provided by the class we are working within and that by using @override
we are adding our own code to what happens when it is called by the operating system. We will also discuss this further in the section Method overloading and overriding confusion later in the chapter. In summary of the image here are the parts of the method signature and their names.
Access Modifier , Return-type , Name of method (Parameters)
And here in the below table are a few examples some that we have used so far as well as some more hypothetical examples laid out in a table to begin to explain them further. We will then look at each one in turn.
Modifier
In our earlier examples, we only used a modifier a couple of times. Partly, because the method doesn't have to use the modifier. The modifier is a way of specifying what code can use (call) your method. We can use modifiers like public and private. Regular variables can have modifiers too. For example:
// Most code can see me public int a; // Code in other classes can't see me private String secret = "Shhh, I am private";
Modifiers (for methods and variables) are an essential Java topic but they are best dealt with when we are discussing the other vital Java topic we have skirted around a few times already- classes. We will do so in Chapter 8, Object Oriented Programming. It helps to have an initial understanding of modifiers at this stage so I just mentioned it.
Return type
Next up is the return type. Like a modifier a return type is optional. So, let's look a bit closer. We have seen that our methods "do stuff", they execute code. But what if we need the results from what they have done? The simplest example of a return type we have seen so far was:
int addAToB(int a, int b){ int answer = a + b; return answer; }
Here the return type in the signature is highlighted above. So the return type is an int
. This means the method addAToB
sends back (returns) to the code that called it a value that will fit in an int
variable. And as you can see in the second highlighted part of the code this is exactly what happens with:
return answer;
The variable answer
is of type int
. The return type can be any Java type we have seen so far.
The method does not have to return a value at all, however. When the method returns no value, the signature must use the void keyword as the return type.
When the void
keyword is used the method body must not attempt to return a value as this will cause a compiler error.
It can, however, use the return
keyword without a value. Here are some combinations of return type and use of the return
keyword that are valid.
void doSomething(){ // our code // I'm done going back to calling code here // no return is necessary }
Another combination is as follows:
void doSomethingElse(){ // our code // I can do this provided I don't try and add a value return; }
The following code is yet another combination:
String joinTogether(String firstName, String lastName){ return firstName + lastName; }
We could call each of the methods above, in turn, like this:
// OK time to call some methods doSomething(); doSomethingElse(); String fullName = joinTogether("Alan ","Turing") // fullName now = Alan Turing // Continue with code from here
The code above would execute all the code in each method in turn.
A closer look at method names
The method name when we design our own methods is arbitrary. But it is a convention to use verbs that make clear what the method will do. Also using the convention of the first letter of the first word of the name being lower case and the first letter of later words being upper case. This is called camel case as we learned while learning about variable names. For example:
void XGHHY78802c(){ // code here }
The above method is perfectly legal and will work. However, what does it do? Let us look at some examples (slightly contrived) that use the convention:
void doSomeVerySpecificTask(){ // code here } void startNewGame(){ // code here } void drawGraphics(){ // code here }
This is much clearer. All the names make it obvious what a method should do and helps avoid confusion. Let's have a look at the parameters in methods.
Parameters
We know that a method can return a result to the calling code. But what if we need to share some data values from the calling code with the method? Parameters allow us to share values with the method. We have already seen an example of parameters when we looked at return types. We will look at the same example but a little more closely at the parameters.
int addAToB(int a, int b){
int answer = a + b;
return answer;
}
Above, the parameters are highlighted. Parameters are contained in parentheses (parameters go here)
right after the method name. Notice that in the first line of the method body we use a + b
:
int answer = a + b;
We use them as if they are already declared and initialized variables. That is because they are. The parameters of the method signature are their declaration and the code that calls the method initializes them as highlighted in the next line of code.
int returnedAnswer = addAToB(10,5);
Also, as we have partly seen in previous examples, we don't have to just use int
in our parameters. We can use any Java type including types we design ourselves(classes). What is more, we can mix and match types as well. We can also use as many parameters as it is necessary to solve our problem. An example might help.
void addToHighScores(String name, int score){ /* all the parameters name score are now living, breathing, declared and initialized variables. The code to add the details would go next. */ }
Of course, none of this matters if our methods don't actually do anything. It is time to talk about method bodies.
Doing things in the method body
The body is the part we have been avoiding with comments like:
// code here
or
// some code
We know exactly what to do in the body already.
Any Java syntax we have learned so far already will work in the body of a method. In fact, if we think back, almost all the code we have written so far has been in a method.
The best thing we can do next is writing a few methods that do something in the body. We will do just that in the Sub Hunter game once we have covered a few more method related topics.
What follows next is a demo app that explores some more issues around methods as well as reaffirming what we already know.
Specifically, we will also look at the concept of method overloading because it is sometimes better to show than to tell. You can implement this mini-app if you wish or just read the text and study the code presented.
Method Overloading by Example
Let's create another new project to explore the topic of method overloading. Notice I didn't say overriding. We will discuss the subtle but significant difference between overloading and overriding shortly.
Creating a new project
Create a new project in the same way as we did for Sub Hunter but call it Exploring Method Overloading
.
Note
The complete code for this mini-app can be found in the download bundle in the Chapter 4/Method overloading
folder.
If you have the Sub Hunter project open now you can select File | New Project and create a project using the following options.
As we did before, be sure the Empty Activity option is selected on the Add an Activity to Mobile screen before clicking Next. Also, be sure to uncheck Generate Layout File and Backwards Compatibility (AppCompat). Don't worry about naming the Activity this is just a mini app to play around with we will not be returning to it.
We will get on with writing three methods but with a slight twist.
Coding the method overloading mini-app
As we will now see, we can create more than one method with the same name provided that the parameters are different. The code in this project is very simple. It is how it works that might appear slightly curious until we analyze it after.
In the first method, we will simply call it printStuff
and pass in an int
variable via a parameter to be printed. Insert this method after the closing }
of onCreate
but before the closing }
of MainActivity
.
void printStuff(int myInt){ Log.d("info", "This is the int only version"); Log.d("info", "myInt = "+ myInt); }
Notice that the code that starts with Log…
has an error. This is because we have not added an import
statement for the Log
class. We could refer to the Sub' Hunter game (or go back to chapter 2) to see what to type but Android Studio can make this easier for us. You might have noticed Android Studio flashes up a quick message as shown in this image.
Click on one of the red-highlighted Log
codes and the message will reappear. Hold the Alt key and then tap the Enter key. Scroll to the top of the code file and notice that the following code has been added:
import android.util.Log;
Tip
As the book progresses I will sometimes suggest using this method to add a class and occasionally I will specifically show you the import…
code to type.
Now we can finish coding the methods.
In this second method, we will also call it printStuff
but pass in a String
variable to be printed. Insert this method after the closing }
of onCreate
but before the closing }
of MainActivity
. Note that it doesn't matter which order we define the methods.
void printStuff(String myString){ Log.i("info", "This is the String only version"); Log.i("info", "myString = "+ myString); }
In this third method, we will also call it printStuff
but pass in a String
variable and an int
to be printed. Insert this method after the closing }
of onCreate
but before the closing }
of MainActivity
.
void printStuff(int myInt, String myString){ Log.i("info", "This is the combined int and String version"); Log.i("info", "myInt = "+ myInt); Log.i("info", "myString = "+ myString); }
To demonstrate that although we can have methods with the same name we can't have methods with the same name and the same parameters, add the previous method exactly the same again and notice the already defined error.
Remove the offending method and we will write some code to see the methods in action.
Now insert this code just before the closing }
of the onCreate
method to call the methods and print some values to the Android logcat.
// Declare and initialize a String and an int int anInt = 10; String aString = "I am a string"; // Now call the different versions of printStuff // The name stays the same, only the parameters vary printStuff(anInt); printStuff(aString); printStuff(anInt, aString);
Running the method overloading mini-app
Now we can run the app on the emulator or a real device. Nothing will appear on the emulator screen but here is the logcat output:
info﹕ This is the int only version info﹕ myInt = 10 info﹕ This is the String only version info﹕ myString = I am a string info﹕ This is the combined int and String version info﹕ myInt = 10 info﹕ myString = I am a string
As you can see, Java has treated three methods with the same name as different methods. This, as we have just proved, can be useful. As a reminder, it is called method overloading.
Note
Method overloading and overriding confusion
Overloading is when we have more than one method with the same name but different parameters.
Overriding is when we replace a method with the same name and the same parameter list as we have done with onCreate
. Note that when you override you also have the option to call the overridden version of the method as we did with onCreate
using the code super.onCreate()
.
We know enough about overloading and overriding to complete this book; but if you are brave and your mind is wandering; yes, you can override an overloaded method but that is something for another time.
How it works
This is how the code works. In each of the steps where we wrote code, we created a method called printStuff
. But each printStuff
method has different parameters, so each is a different method that can be called individually.
void printStuff(int myInt){ ... } void printStuff(String myString){ ... } void printStuff(int myInt, String myString){ ... }
The body of each of the methods is trivial and just prints out the passed in parameters and confirms which version of the method is being called currently.
The next important part of our code is when we make it plain which we mean to call by using the appropriate arguments that match the parameters in the signature. We call each, in turn, using the appropriate parameters so the compiler knows the exact method required.
printStuff(anInt); printStuff(aString); printStuff(anInt, aString);
Let's explore methods a little further and look at the relationship between methods and variables.