DEVTOME.COM HOSTING COSTS HAVE BEGUN TO EXCEED 115$ MONTHLY. THE ADMINISTRATION IS NO LONGER ABLE TO HANDLE THE COST WITHOUT ASSISTANCE DUE TO THE RISING COST. THIS HAS BEEN OCCURRING FOR ALMOST A YEAR, BUT WE HAVE BEEN HANDLING IT FROM OUR OWN POCKETS. HOWEVER, WITH LITERALLY NO DONATIONS FOR THE PAST 2+ YEARS IT HAS DEPLETED THE BUDGET IN SHORT ORDER WITH THE INCREASE IN ACTIVITY ON THE SITE IN THE PAST 6 MONTHS. OUR CPU USAGE HAS BECOME TOO HIGH TO REMAIN ON A REASONABLE COSTING PLAN THAT WE COULD MAINTAIN. IF YOU WOULD LIKE TO SUPPORT THE DEVTOME PROJECT AND KEEP THE SITE UP/ALIVE PLEASE DONATE (EVEN IF ITS A SATOSHI) TO OUR DEVCOIN 1M4PCuMXvpWX6LHPkBEf3LJ2z1boZv4EQa OR OUR BTC WALLET 16eqEcqfw4zHUh2znvMcmRzGVwCn7CJLxR TO ALLOW US TO AFFORD THE HOSTING.

THE DEVCOIN AND DEVTOME PROJECTS ARE BOTH VERY IMPORTANT TO THE COMMUNITY. PLEASE CONTRIBUTE TO ITS FURTHER SUCCESS FOR ANOTHER 5 OR MORE YEARS!

Methods in Java

One thing you may have noticed in your solution to the Nim coursework is that you are repeating (or nearly repeating) chunks of code – the code for player 1 and player 2 are probably pretty similar. It would be nice to only write this code once. Also, even if code is not repeated, programs can get complex and it would be nice to be able to break them into smaller chunks, or subprograms, which are easier to manage and understand. Obviously, since there’s a chapter on it☺, a way of doing this exists. Also we’ve seen some examples already.

•	System.out.println(“Lemon sorbet”); - prints Lemon Sorbet to the screen.
•	int value = in.nextInt(); reads in an integer from the keyboard.

In fact, all the things we’ve seen that end in (..) – are examples of these kinds of subprograms.

Names – Procedures, Functions and Methods

If you look at the two examples above you can see that they are conceptually different – the first one (System.out.println) just goes away and does something; the second one (in.nextInt) does something and returns a value (in this case an integer) to the program that called or invoked it. Historically, the first kind of subprogram is called a procedure and the second kind a function (and is closely related to mathematical functions). However, in object oriented programming languages like Java we just call them all methods.

Simple Example – the Square Method

Suppose we are writing a program that needs to compute the squares of integers many times and we want to write a method to do it:

static int square(int num) {
	return num * num;
}

The first line defines the signature of the method – what it’s called, the type of data that it returns, and the types of the arguments or parameters: that is, the data that it will work with. In the example above, the method is called square, it returns and int, and it has one argument/parameter, which is an int (int num). As well as the type of the argument, it also has a name (num) – this is used inside the body of the method to refer to the value of the parameter. As this method returns a value, we must use the keyword return to specify what that value is – in this case, num*num.

KEY POINT – Static

Notice that we’ve used the word static at the start – I’m not going to explain this yet but, for now, you need to put it in (try taking it out and see what the compiler says).

We call the square method like this:

int squareValue = square(5);   -  squareValue == 25
System.out.println(square(3)); -  prints 9
int val = 4;
int result;
result = square(val);          -  result == 16

int newVal = 6;
result = square(val + newVal); -  result now == 100

As you can see it’s quite flexible – we can: • Use numbers (literals) like 4 directly as arguments. • Use variables as arguments (note: they do not need to have the same name as the one we use inside the method definition). • Use more complex expressions (like a + b) as arguments. • Assign the result to a variable. • Use it directly – for example, print it out.

Here’s a complete program to show you where the method definition goes.  

import java.util.Scanner;

class SquareExample {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		
		System.out.print("Number? ");
		while(in.hasNextInt()) {
			int squareValue = in.nextInt();
			int result = square(squareValue);
			System.out.println("The square of "
+ squareValue +
			     " is " + result);
			System.out.print("Another number “ +
(anything else to quit)? ");
		}	
	}
	
	static int square(int num) {
		return num * num;
	}	
}

This version uses two int variables – one for the argument and one for the result. But we can actually simplify the while loop by calling the method in the print statement:

while(in.hasNextInt()) {
			int squareValue = in.nextInt();
			System.out.println("The square of " 
   + squareValue +
			   " is " + square(squareValue));
			System.out.print("Another number “ +
(anything else to quit)? ");
		}

Another Example – returning Yes or No

Recall that we’ve seen some code that repeatedly asks a user to enter a response until they say yes or no. You can imagine that you could need to do that a lot in a program, meaning it’s an ideal choice for a method:

static String yesOrNo() {
	Scanner in = new Scanner();
	String inValue;

	do {
		System.out.print(“Enter yes or no: “);
		inValue = in.nextLine();
	while(!inValue.equalsIgnoreCase(“yes”) &&
		 !inValue.equalsIgnoreCase(“no”));
	return inValue;
}

Notice this one – as well as being much more complex – has no parameters. This is OK – but we still need to put the brackets in even though there’s nothing between them. The way we call this is:

String result = YesOrNo();

or

do {
	- some stuff
while(YesOrNo().equals(“yes”));
<pre>
Notice that our principle of D.R.Y. – Don’t Repeat Yourself – is being violated here and we should have constants (final variables) for ‘yes’ and ‘no’. In fact a better version might be:
<pre>
static String yesOrNo(String stopVal, String contVal) {
	Scanner in = new Scanner();
	String inValue;

	do {
		System.out.print(“Enter “ + stopVal + “ or “ +
contVal + “: “);
		inValue = in.nextLine();
	while(!inValue.equalsIgnoreCase(contVal) &&
		!inValue.equalsIgnoreCase(stopVal));
	return inValue;
}

 

and then do something like this when we call it:

final String CONT_VAL = “yes”; - or “ja”, or “ναί”, or…
final String STOP_VAL = “no”;  - or “nein”, “όχι”, or…

do {
	- some stuff
while(YesOrNo(STOP_VAL, CONT_VAL).equalsCONT_VAL));

Notice also that this version has two arguments – and they are written like this:

typeName argumentName, typeName argumentName,…

for as many arguments (of different types, or the same types) as you need.

Methods that Don’t Return Results

The first example we showed above (System.out.println) didn’t actually return anything – it just does something. In some languages sub-programs that return results and those that don’t are separate things – but in Java, those that don’t return results are just a special case: instead of putting in a return type like int or String, we use the special ‘no return value’ type (or keyword) void. For example:

static void sayHello(String name) {
	System.out.println(“Hello “ + name + “!”);
}

Notice the word void in place of the return type names we used in the examples above. Also notice that there is no return statement – because it doesn’t return a result. To call this method, we can do:

sayHello(“Mr President”);

String name = “Dave”;
sayHello(name);

String forename = “Darth”;
String surname = “Vader”;
sayHello(forename + “ “ + surname);

As before you can see that there are lots of options for how you construct the arguments – they can be constants, variables, or operations based on constants and/or variables.

Parameters and What You Can/Can’t Do

Let’s go back to our first example

static int square(int num) {
	return num * num;
}

Now we’ve seen void methods, it seems we could change it to this:

static void square(int num) {
	num = num * num;
}

That is, instead of returning the new value, we’re changing (or trying) the value of the parameter we passed in. Does this work? Here’s a complete program with both methods (we’ve changed the name of one because you can’t have two methods with the same name in this case):

class SquareTests {
	public static void main(String[] args) {
		- The one that returns an int
		int number = 5;
		int squareVal = square(number);
		System.out.println("The 'returning' method: "
             + squareVal);
		
		- The one that tries to change it's argument
- Try to change number to number*number
		squareVoid(number); 
		System.out.println("The 'void' method: "
             + number);
		
	}
	
	static int square(int num) {
		return num * num;
	}
	
	static void squareVoid(int num) {
		num = num * num;
	}
}

The result you should get from this is:

The 'returning' method: 25
The 'void' method: 5

That is, the second void method does not change the value of the parameter. This is because parameters are copies – not the original variables. Try changing the program by putting a println statement into the void method:

static void squareVoid(int num) {
num = num * num;
System.out.println(“Inside ‘void’ method: “ + num);
}

You should see:

The 'returning' method: 25
Inside 'void' method: 25
The 'void' method: 5

As you can see the value of num inside the method does change – but because it’s a copy, and that copy is thrown away after the method finishes, the original parameter does not change.

If you think about it, if the value of a parameter could change outside the method then you couldn’t do things like:

squareVoid(5);

because this would mean trying to change the value of 5 to 25 – which doesn’t make sense. On the other hand:

int result = square(5);

does – because it returns a new value and doesn’t try to change the parameter. What actually happens when you execute:

squareVoid(5);

is: • A copy is made of the value 5; • that value is copied into a new variable num inside the method; • that new variable is squared and becomes 25; • if we are using the version which has the println in it, that new value of 25 is printed out; • finally, the new variable is thrown away when the method ends.

KEY POINT: Parameters are Copies

When you pass a parameter to a method, a copy is made and the method works on the copy. This means you cannot change the value of simple types like int, double, boolean which are passed as parameters – you can only return new values. It specifically says simple types in the previous sentence because the situation with complex types – like ArrayLists – is more complex as we’ll see below.

Advanced Aside: Call by Value

The way parameters are passed in Java is known as Call by Value – because it is the value that is passed, not the actual variable. This is the simplest parameter passing mechanism, and the way Java does it, the only one needed. But there are other ways of doing it: • Call by Reference – the actual variable is passed, so you can change it. Java doesn’t do this but has a way of ‘simulating’ it (see later). • Call by Value-Result – the parameter is passed as a copy, but then the value is copied back out to the original variable. Java doesn’t do this (most languages don’t). In most cases this behaves the same as Call by Reference except in some obscure cases.

Passing Complex Data

We specifically said about that you cannot change the value of parameters outside methods, and we showed an example. We also said that things were not so simple with more complex data. Here’s an example:

import java.util.ArrayList;
import java.util.Arrays;

class ChangeArrayList {
	public static void main(String[] args) {
		ArrayList<Integer> list =
			new ArrayList<>(Arrays.asList(1, 2, 3));
		
		addOne(list);
		
		for(int val : list) {
			System.out.println(val);
		}	
	}
	
	static void addOne(ArrayList<Integer> valList) {
		for(int i = 0; i < valList.size(); i++) {
			valList.set(i, valList.get(i) + 1);
		}
	}
}

(As an aside, notice how we quickly populate an ArrayList with a set of values.) If you run this you’ll find the output is 2, 3, 4 – but we put 1, 2, 3 in the ArrayList – so obviously we’ve managed to change it.

  Here’s a contradictory example:

import java.util.ArrayList;
import java.util.Arrays;

class ChangeArrayList {
	public static void main(String[] args) {
		ArrayList<Integer> list =
			new ArrayList<>(Arrays.asList(1, 2, 3));
		
		zapArrayList(list);
		
		for(int val : list) {
			System.out.println(val);
		}
		
	}
		
static void zapArrayList(ArrayList<Integer>
   valList){
		valList = null;
	}
}

This one has a method that explicitly sets the ArrayList passed in as an argument to null – i.e. it basically deletes it. And yet, after the method returns the contents of the ArrayList are still present.

The Answer? Complex Objects are References

If you didn’t read it the first time, now go back to Chapter 4 – Conditionals, and read the section Strings and Equals Danger: Skip on First Reading if New to Programming and especially look at Figure 1. Here’s a version of that picture that will help:

Figure 1: References to Data

Complex data like an ArrayList is not stored in the same way as simple data. Instead the actual variable is a reference to the real data, which is elsewhere in memory. This is done for a perfectly good reason, which I’m happy to explain to anyone who wants to know, but is a bit off topic here. What this means is that the reference is the value that is passed – it cannot be changed (which why zapArrayList didn’t work) but the data it refers to can be changed (which is why addOne did work).

The main Method

Hopefully after reading this, the line you’ve been writing for weeks now should make a bit more sense:

public static void main(String[] args)

We still need to deal with public and static, but:

void main(String[] args)

should now be clear – this is a method, called main with one argument which is an array of Strings. The convention in Java (and other languages like C, C++ and C# - most widely-used modern languages) is that execution starts at the method called main. The main method is just an ordinary method like any other, and it’s possible to pass in parameters. Here’s a program that deals with parameters:

class SquareArgs {
	public static void main(String[] args) {
		
		if (args.length == 0) {
			System.out.println("Supply a number");
		} else {
			int value = Integer.parseInt(args[0]);
			System.out.println("Square " +
                 square(value));
		}
	}
		
	static int square(int num) {
		return num * num;
	}	
}

The if statement first checks to see if there are any arguments because if args.length==0 then the array args is empty and the user hasn’t typed any values in. Otherwise we: • Assume that the first thing they’ve typed is the argument and ignore anyting else they might have written after it (args[0] – remember that arrays in Java start with element zero). • Users Int.parseInt to turn the string into an integer – this will crash if the user types a non-integer, which is not great but this is meant to be a simple example.

The way we ‘call’ the main method with parameters is slightly different to the way we would call it within a program and we don’t use brackets:

java SquareArgs 5
Square 25

if we leave off the arguments:

java SquareArgs
Supply a number

if we type extra numbers, they are ignored:

java SquareArgs 7 3
Square 49

Guidelines for Methods

Like anything in programming deciding how to break a problem into methods takes practice. However, here are some guidelines. • Repeated Code – this is an obvious case: if your program has repeated code then it makes sense to put it in a method. • Single Task – methods should do one thing: keep them simple and focused on solving a single problem. If you find yourself saying ‘this is a method to do X and then Y’, make it two methods – one to do X, and one to do Y. • Keep Small – methods should be short: a rule of thumb is 25 lines of about 80 columns of text. You need to be a bit flexible and sometimes you need to go over this: but in general, methods that are bigger than this get to be hard to understand. • Use Parameters – a method is easiest to understand if it only deals with information that is supplied by parameters because that makes it self-contained and easier to understand. Try to avoid what are commonly called ‘global’ variables. • Not Too Many Parameters – this slightly conflicts with the point above; but don’t overdo the parameters: if you get more than about five then your method is probably too complicated so consider breaking into smaller ones (even if it’s already quite short). • Logical Breakdown – as you think about the problem you are trying to solve, and break it into parts, you can’t go far wrong if you start by making each part a method. These are simple rules of thumb, and sometimes you’ll have to break them; also as you get more experienced, you will see more cases where you should break them. But they apply most of the time to most problems.

Commenting Methods

Something I haven’t done here (oops…) is to put a brief comment at the start of each method saying what it does.

Computer Science


QR Code
QR Code methods_java (generated for current page)
 

Advertise with Anonymous Ads