Once again, welcome back! The topic for today is Java methods. If you have not been following along, we have covered logic and binary, primitive types, and reference types. Now, we’re going to dive into one of the features of objects called method. In particular, we are going to focus on method creation and usage in Java.
Table of Contents
What Are Methods?
If you’ve had a chance to play with strings, then you’re probably familiar with a few methods like length()
and equals()
. In fact, last lesson we covered methods very briefly when we mentioned the dot operator.
As it turns out, methods are actions we perform on objects. In the case of strings, we’ve used methods to learn about various String
objects. For instance, we can use length()
to determine the number of characters in a String
object.
Of course, methods can do other things like manipulate objects. Since strings are immutable, there typically aren’t many of those kinds of methods. However, they do exist.
For instance, there is a String
method called toLowerCase()
which will convert all the characters in a String
object to lowercase. As already mentioned, strings are immutable, so the method will actually return a brand new String
object—one with a new address. Let’s take a look:
String myString = "Hello, World!"; myNewString = myString.toLowerCase(); System.out.printLn(myString); // Prints "Hello, World!" System.out.printLn(myNewString); // Prints "hello, world!"
Notice how the two strings are different. That’s because the original String
object was never changed. Instead, the toLowerCase()
method returned an entirely new String
object. We’ll dig more into this later.
Until then, we should probably touch on an important Computer Science topic: the stack.
The Stack
As we continue these lessons, we will want to become familiar with two key areas of computer hardware: the stack and the heap. These two concepts define regions of memory where a program lives.
When we talk about methods, we really only care about the stack. The stack is a last-in-first-out (LIFO) data structure which closely resembles something like a pancake stack (except it is often drawn upside down). As you might imagine, the last pancake added to the stack gets eaten first.
Every time we use a method, it gets added to the stack as a method call. For instance, if we call equals()
on a String object, a new method call will get added to the stack. Once equals()
is finished computing, the call pops off the stack, and we’re back to an empty stack.
For the sake of argument, let’s say that equals()
calls a few methods of its own. For instance, maybe it compares the two strings by length. If they’re the same length, we’ll say the strings are equal. To do this, equals()
would have to wait for length()
to finish computing on both strings before it could return true
or false
.
Fortunately, the stack simplifies the complexity a bit. The program will begin by pushing the equals()
call onto the stack. Then, the program will push the length()
call for the first string on top of the equals()
call.
Once length()
is finished, it will pass the result to equals()
, and the program will push an additional length()
call on top of the equals()
call for the second string. Finally, equals()
will use the result of both length()
calculations to report true
or false
.
Don’t worry if this seems over your head! We will revisit stacks in the future.
A Tale of Two Methods
In general, there are two basic types of methods: static and instance. So far, we have been working with instance methods which operate on an instance of a class. For example:
String favoriteColor = "blue"; String favoriteFood = "sushi"; favoriteColor.equals(favoriteFood);
Here, we have two instances of the class String
: favoriteColor
and favoriteFood
. The first thing to note is that the equals()
method makes use of both of these variables. However, only one of these variables is supplied as an input while the other is initiating the method call.
So how does this work?
Instance methods work because the object they are a part of provides context to the operation. In other words, we only need one input for the equals method because it has access to the calling object.
Meanwhile, a static method belongs to the class only. In other words, static methods do not have access to instance properties like the character sequence that defines favoriteColor
. Instead, static methods provide utility to a class as a whole. For instance:
String.valueOf(5);
This line calls the static method valueOf()
from the String
class on an integer. The result is the integer as a String
or “5.” Static methods work without ever needing an instance of a class because they don’t need any context to perform their function.
Method Syntax
Now that we have an idea of how methods are used, let’s take a look at how they are written.
Static Method Syntax
First, let’s look at a static method:
public static int add(int firstNum, int secondNum) { return firstNum + secondNum; }
In the first line, we have quite a few pieces of information:
public
: indicates that the method is public (we can discuss this later)static
: declares the method as staticint
: indicates a return type of integer
The next bit is the method name followed by parentheses which contain the inputs. In this case, we have created an add method with two int
inputs.
Inside the body of the method, we have the return statement. The return
keyword marks an exit point for the method. In other words, return takes whatever is on its line and returns it as output from the method. If we were to call add(5, 6)
, the return line would give us a result of 11.
You may also notice that there are a couple of pieces of interesting syntax. For instance, the line in the body ends with a semicolon, ;
. In Java, all lines must end with a semicolon unless they open a code block. Code blocks are opened using an open brace, {
, and closed using a close brace, }
.
Instance Method Syntax
Unfortunately, until we talk about class layout, we won’t have any static method examples for you to try on your own. However, you can try writing your own instance methods which will serve a similar purpose for now.
Instance method syntax is exactly the same except you exclude the static
modifier. For instance:
public int add(int firstNum, int secondNum) { return firstNum + secondNum; }
Try entering this directly into the DrJava interactions pane. If successful, you should be able to call the method using its name and two inputs of your choice. Try using your knowledge of the primitive types to observe the method under various inputs. Some examples might include:
add(2, 3) add(-4, 6) add(4.0, 7) add(5, true)
Notice how some of these examples fail where they might not have using the addition operator. That’s because our method enforces integer input only.
So what makes this method different from a static method?
At the moment, nothing. However, try the following:
int firstNum; int secondNum; public int add() { return firstNum + secondNum; }
Now you can call add without any inputs. Without explicitly defining firstNum
and secondNum
, add()
should return 0. However, you can change the behavior of the add method by changing either of those variables. Try modifying firstNum
and monitoring the behavior of add.
Method Overloading
Without realizing it, you have just overloaded the method add. Not sure what that means? Try calling add both ways: once with no inputs and once with two inputs.
You have just created two different add methods that both share the same name. This is perfectly legal in Java, and it allows us to expand the behavior of a single method to handle multiple input parameter lists. There are many uses for method overloading, but in general it is used to improve code readability.
In the future, we will definitely tackle code readability as I feel it is the most important aspect of coding. For now, we’re going to stop here. Thanks again for sticking around! Up next we’ll be widening our scope to class structure, so we can start writing our own code.
Recent Code Posts
This week, we're hitting another beginner topic: the assignment operator. While the idea is simple, the concept is rich in related ideas like scope, iterable unpacking, and augmented assignment.
In the world of programming languages, expressions are an interesting concept that folks tend to implicitly understand but might not be able to define. As a result, I figured I'd take a crack at...