Learning Java by Building Android  Games
上QQ阅读APP看书,第一时间看更新

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.

Methods revisited and explained further

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 and 4.
  • In the method signature, the value 2 is assigned to int a and the value 4 is assigned to int b.
  • Within the method body, the variables a and b are added together and used to initialize the new variable int 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.

The method signature

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.

Return type

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.

Coding the method overloading mini-app

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.

Coding the method overloading mini-app

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.