Time estimate: 90 min.

2.1. Using Methods

Computer programs, as you’ll discover, can get rather complex. Large programs may consist of millions of lines of code, all of which have to connect to each other in just the right way for the program to work. Compare that to a novel, which might be only a few hundred thousand words and is still perfectly readable even if the author misplaced a semicolon. Yet people manage to write those large programs. How can human beings possibly deal with that kind of complexity?

Abstraction is the main tool we have to keep complexity under control. Abstraction gets a bad rap in our culture, often being considered synonymous with “hard to understand” as in abstract math or abstract theoretical physics. But abstraction really just means “hiding details” and we use abstractions all the time without even realizing it.

When you learn to drive, you learn how to use a steering wheel and the brake and accelerator pedals. But your drivers ed instructor will almost certainly not teach you about how a rack and pinion turns the wheels or how tiny explosions in the car’s engine make it go. Most of the details are abstracted away. A car mechanic needs to understand them but as a driver you can deal with the car in terms of a much simpler set of concepts like press the gas to make the car go.

Abstraction is a powerful tool that will appear over and over as you learn to program. In this chapter we are going to talk about procedural abstraction which just means hiding the details of how something gets done so we can focus instead on what the procedure achieves.

Procedures go by many names in different programming languages: functions, subroutines, commands, or even procedures. In Java they are called methods, as in a method of doing something.

In this section we are going to talk about using already existing methods as useful abstractions. In the next section we’ll learn how to write new methods and create our own abstractions.

2.1.1. What is a Method?

Toaster

One way to think of a method is as a little machine that does something. Consider a toaster. You put bread in the toaster and turn it on and a little while later you get a piece of toast. Toasters don’t make toast without bread but they can make toast out of any kind of bread that fits in the toaster.

And even a lowly toaster is a kind of abstraction. While you probably have some rough idea of how a toaster works on the inside, as with a car you don’t have to know how it works to use it. All you have to know is, you put the bread in, you press the button, and a little while later your toast pops out.

The other thing about a toaster is you only need one—you don’t need to get a new toaster every time you want to make a piece of toast. Once you’ve got a toaster you can use it to make all the toast you want.

Methods are similar. Most methods, like the toaster, require some inputs, which we call arguments, and then either produce an effect in the world or compute a value based on the arguments. Effects can be things like printing on the screen and computed values can be anything a computer can represent, numbers, booleans, images, videos, the text of a novel, models of the universe—pretty much anything.

Methods also hide, i.e. abstract, the details of what exactly they are doing which lets us write code without having to worry about those details. And once you’ve got a method that does something useful, you can use that method whenever you need to do that thing, rather than writing out the same code over and over.

Two methods we’ve used so far in this book are System.out.println and System.out.print. Their job is to take a value and print it to the screen, either with or without a new line at the end. They both abstract the actual process of turning the value into a textual representation and then causing that text to be displayed on the screen. There’s a lot of things that have to happen under the covers to make that text come out on the screen but thankfully we don’t have to know any more about that than we have to know about how pressing on a car’s accelerator makes it go.

Now let’s consider the other kind of method, those that compute new values from their arguments. We’ll start with some methods from the Math class that comes with Java but remember that methods are useful for all kinds of computation, not just doing math.

A simple example of this kind of method from Math is the sqrt method. Math.sqrt takes a double argument and returns a double value which is the square root of the argument. We say that the return type of sqrt is double. For example, the square root of 9 is 3 because 3 squared is 9.

Note

Math.sqrt is a static method, sometimes called a class method. The name of the method is just sqrt but we refer to it with the name of the class where it is defined, Math, followed by a dot (.) followed by the name. If you write a static method in your own class, as you will do later in this unit, you can call the method using just the name of the method.

Methods that return a value typically don’t have any observable effect when they are called—nothing shows up on the screen; no lights blink, no bleeps or bloops are emitted—they just produces a new value that you need to something with like assigning it to a variable. For instance, we might use sqrt like this:

double root = Math.sqrt(2);

After this assignment, root will have the value 1.4142135623730951 which is the double value that most closely approximates the value of the square root of 2. (If you remember your 8th grade math, you know that the square root of 2 is a irrational number, meaning its decimal representation is infinite. Since doubles have to be represented in a finite amount of memory we have to live with the approximation. If you don’t remember that fact from 8th grade, perhaps your 8th grade math teacher neglected to tell you that the Greek mathematician who proved that the square root of 2 is irrational was, according to legend, drowned at sea by a bunch of other Greek mathematicians who were outraged at the notion that any numbers could not be represented using ratios. That story probably would have stuck in your head.)

You could also use the value returned by a method as the argument to another method. For instance, if you wanted to print the square root of two you could write:

System.out.println("The square root of 2 is: " + Math.sqrt(2));

If System.out.println is a car, using all kinds of machinery under the hood to make it go, Math.sqrt is maybe more like a toaster. It’s not a total mystery how it works but few of us probably know off the top of our head how to compute arbitrary square roots efficiently. So it sure is convenient that Java provides this method for us. (In fact, the source code for sqrt in the JDK is over 100 lines of pretty hairy math code.)

2.1.2. Calling Methods

The main thing we can do with a method is call it. Calling a method is how we get the code in the method to run, either to produce an effect or to compute a value. There are several examples above of calling both println and sqrt but to spell it out, a call to a method is made up of the name of the method followed by a pair of parentheses. In between the parentheses is a comma-separated list of expressions, one for each parameter defined in the method declaration.

Thus when we want to use a method that computes a value we need to know four things:

  • Its name.

  • What arguments it expects.

  • What type of value it returns.

  • How the returned value is related to the arguments.

If you look back to the earlier description of Math.sqrt, you’ll see it includes exactly that information: “Math.sqrt takes a double argument and returns a double value which is the square root of the argument.” (If you look at the official documentation you’ll see they are even more precise: it is documented to return the “correctly rounded positive square root of a double value.”)

All the methods we’ve talked about so far only take one argument so the argument lists only contain one expression. In a moment we’ll learn about some methods that take more than one argument and also one that takes no arguments.

exercise Check Your Understanding

Note that some methods take no arguments but when you call them you still need an empty pair of parentheses. It’s a common beginner mistake to leave the parentheses off a method call that takes no arguments. System.out.println can, in fact be called with no arguments to just print a blank line. With that in mind, compare these two lines of code:

System.out.println(); // ✅ Good - prints a blank line
System.out.println;   // ❌ No good - will give you a compile error.

2.1.3. Other Math methods - abs and pow

Pow!

Math.sqrt is just one of many static methods in the Math class. Several other Math methods are part of the AP subset in addition to sqrt: abs, pow, and random. And if you need to do complex mathematical computations in a program you are writing, you should take a look at what else is in Math—it might have just what you need. You can read about all the methods in the Javadocs.

Note

All the Math methods that you may need to use or understand on the AP exam are listed in the AP CSA Java Quick Reference Sheet that you can use during the exam.

You may be able to guess what abs and pow do, if you can decipher the abbreviations. Math.abs takes a single argument, either a double or an int and returns a value of the same type which is the absolute value of the argument. The absolute value of a number is its positive value without its sign. You can also think of it as the distance the number is from 0. So:

Math.abs(45);    // returns 45
Math.abs(-45);   // returns 45
Math.abs(33.3);  // returns 33.3
Math.abs(-33.3); // returns 33.3

The next method Math.pow takes two arguments, both doubles, and returns a double which is the first argument raised to the power of the second argument.

Math.pow(2 , 3); // returns 8.0
Math.pow(10, 6); // returns 1000000.0
Math.pow(2, -3); // returns 0.125

exercise Check Your Understanding

2.1.4. Other Math methods - random

Dice

The Math.random method is a bit of an odd duck. It takes no arguments and each time it is called returns an essentially random double value that is greater than or equal to 0 and less than 1. (Technically the numbers returned are called “pseudo random numbers” since they are not actually truly random, being generated by a deterministic computer program.) It’s not a method like println that is called purely for an effect, but it’s also not a method like sqrt or pow that returns a value computed purely from its arguments—it doesn’t even take any arguments! Maybe rather than an odd duck it’s a platypus, a mammal that lays eggs.

What’s going on is there a hidden value that is used to determine what number is returned each time random is called and random changes that value each time it is called so the next time it is called it will return a new unpredictable number.

There are lots of uses for random numbers in programs. Games often use random numbers to generate different possibilities and there are many kinds of simulations that depend on randomly generating possible outcomes.

For some applications you may need to use other, more special purpose sources of randomness but for simple cases, Math.random is just the thing, so it’s handy to know a few recipes for generating various kinds of random numbers starting from the random output of Math.random.

Before we start, let’s introduce one very handy bit of terminology that you may or may not have heard of before. When we talk about ranges of numbers sometimes we need to be precise about whether the ends of the range are part of the range. For example, Math.random returns a number between 0.0 and 1.0, but does that mean it can return exactly 0.0? Or exactly 1.0? As it turns out it can return 0.0 but never returns 1.0.

When we need to be precise about this we’d say that it returns a number between 0.0, inclusive, and 1.0, exclusive, meaning include 0.0 but exclude 1.0. Lots of ranges in Java are expressed this way, as you’ll see later on with an inclusive bottom and an exclusive top.

With that out of the way, let’s take a look at the values we get from just calling Math.random:

coding exercise Coding Exercise

Try out the following code. Run it several times to see what it prints each time.

Getting a number between 0.0, inclusive, and 1.0, exclusive, may not seem all that useful. But we can expand the range easily enough. To see how, imagine you had less than a dollar to your name and you wanted to be richer—you’d want to find a way to multiply your money. If you could invest every penny you had in something that would multiply your money by 1,000, then instead of having somewhere between $0 and $1 you’d have somewhere between $0 (inclusive—if you started with $0) and $1,000 (exclusive, since if you had even a fraction of a penny less than $1 multiplying by 1,000 would still leave you just a bit shy of $1,000.) If the investment multiplied your original money by a million, you’d have between $0 and $1,000,000! (But never quite $1,000,000.)

Same trick applies to random numbers. The value Math.random returns is like the initial amount of money in your pocket, always a bit less than $1. If you multiply that value by any amount, it will stretch it into the range you want:

coding exercise Coding Exercise

Try out the following code. Run it several times to see what it prints each time. Did you ever see 0.0? How about 10.0?

You may have noticed that while the numbers generated were always in the range 0.0 to 10.0, all the numbers probably had a lot a digits after the decimal point. Often we want a random integer, with nothing after the decimal point. Easy enough—recall that casting a double to an int will throw away any values after the decimal point.

coding exercise Coding Exercise

Try out the following code. Run it several times to see what it prints each time.

What happens if you take away the parentheses around (Math.random() * 10)? Does it change the kinds of numbers that are printed? Can you explain why?

Also, does this program ever print 10? Why or why not?

Finally, what if we want a number in a range that doesn’t start with 0, say a number from 1 to 10 (including 10) instead of from 0 to 9 (including 9)? Since the size of the two ranges is the same, with ten numbers in each, all we need to do is shift from the range we’ve generated into the range we want. In other words, add the difference between the two ranges, 1 in this case.

coding exercise Coding Exercise

The code below generates numbers in the same range as the previous, 0-9, inclusive.

Run it several times to convince yourself that’s true.

Then modify the code to change the range generated to be 1-10, inclusive. Run it enough times to convince yourself it never returns 0 and does return 10.

So the general recipe for generating a random is to first stretch the value from Math.random() until it’s in the right size range by multiplying by the size of the range. Then if you want an integer value, cast to int to discard the part after the decimal point. Then shift the value up by adding the minimum value. The table below shows some applications of that general recipe.

Random recipes

Expression

Minimum (inclusive)

Maximum (exclusive)

Possible values

Math.random()

0.0

1.0

Over 9 quadrillion

Math.random() * 100

0.0

100.0

Over 9 quadrillion

(int)(Math.random() * 100)

0

100

100

(int)(Math.random() * 50) + 25

25

75

50

(int)(Math.random() * 11) - 5

-5

5

11

(int)(Math.random() * max)

0

max

max

(int)(Math.random() * range) + min

min

min + range

range

(int)(Math.random() * (max - min)) + min

min

max

max - min

exercise Check your understanding

exercise AP CSA Sample Problem

2.1.5. Summary

  • Abstraction means hiding details. Procedural abstraction means hiding the details of how a procedure is performed and so we can focus on what it achieves.

  • A method in Java either computes a value or has some effect, such as printing to the screen.

  • Static methods can be referenced using the name of the class they are defined in, a dot, and the name of the method, e.g. Math.sqrt to reference the sqrt method defined in the class Math.

  • The following static Math methods are part of the AP CSA Java Quick Reference Sheet:

    • int abs(int): Returns the absolute value of an int value (which means no negatives).

    • double abs(double): Returns the absolute value of a double value.

    • double pow(double, double): Returns the value of the first parameter raised to the power of the second parameter.

    • double sqrt(double): Returns the positive square root of a double value.

    • double random(): Returns a double value greater than or equal to 0.0 and less than 1.0 (not including 1.0)!

  • The values returned from Math.random can be manipulated to produce a random int or double in a desired range.

You have attempted of activities on this page