Lesson Weekend

So far, we have only used methods built directly into Java. But the real power of programming is in writing our own methods! In this lesson we'll explore how to create our own methods in Java, and implement these methods into our programs. Much of this will be review from Intro to Programming. But take special care to focus on the Java-specific concepts we cover.

Application Setup

Let's create an application that asks users what they ate for various meals. As before, we'll use our BufferedReader, print a question to the user, gather their response, and respond to them.

Create a new meal-time project, as you have before, and a new MealTime.java file.

src/main/java/MealTime.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class MealTime {

   public static void main(String[] args) {
       try{

           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
           System.out.println("What did you eat for breakfast?");
           String yourBreakfast = bufferedReader.readLine();
           System.out.println("You had " + yourBreakfast + " for breakfast.");

       }
       catch(IOException e)
       {
           e.printStackTrace();
       }
   }
}

This should look pretty familiar. We're simply asking the user what they ate, gathering their response, then returning it in a concatenated sentence.

Let's also ask our user about lunch and dinner! We'll add the following:

src/main/java/MealTime.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class MealTime {

   public static void main(String[] args) {
       try{

           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));


           System.out.println("What did you eat for breakfast?");
           String yourBreakfast = bufferedReader.readLine();
           System.out.println("You had " + yourBreakfast + " for breakfast.");

           System.out.println("What did you eat for lunch?");
           String yourLunch = bufferedReader.readLine();
           System.out.println("You had " + yourLunch + " for lunch.");

           System.out.println("What did you eat for dinner?");
           String yourDinner = bufferedReader.readLine();
           System.out.println("You had " + yourDinner + " for dinner.");

       }
       catch(IOException e)
       {
           e.printStackTrace();
       }
   }
}

Hmm, don't you feel like a lot of this code is redundant? Even though we're asking the user about three separate meals, there's a lot of shared code. As you learned in Intro to Programming, similar code being used in multiple areas should be packaged in a reusable method.

Writing Methods

Again, the general concept of writing methods should be review, but let's explore what this specifically looks like in Java.

We'll define a method called askWhatYouAteFor() to handle asking our user about various meals. Instead of repeating similar code, we'll simply call this method:

src/main/java/MealTime.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class MealTime {

   public static void main(String[] args) {

       try{

           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));


           System.out.println("What did you eat for breakfast?");
           String yourBreakfast = bufferedReader.readLine();
           System.out.println("You had " + yourBreakfast + " for breakfast.");

           System.out.println("What did you eat for lunch?");
           String yourLunch = bufferedReader.readLine();
           System.out.println("You had " + yourLunch + " for lunch.");

           System.out.println("What did you eat for dinner?");
           String yourDinner = bufferedReader.readLine();
           System.out.println("You had " + yourDinner + " for dinner.");

       }
       catch(IOException e)
       {
           e.printStackTrace();
       }

   }

   public static void askWhatYouAteFor(String meal){
       System.out.println("What did you eat for " + meal + "?");
   }
}

Anatomy of a Custom Java Method

Great, we have a method! Let's look at the process for method creation a little more closely.

src/main/java/MealTime.java
...
  public static void askWhatYouAteFor(String meal) {
    System.out.println("What did you eat for " + meal + "?");
  }
...

The following are all requirements of custom Java methods, as we see in the example above:

  1. Type declaration. The public static void portion of this method declaration should look pretty familiar. It's very similar to the boilerplate we use to set up a program. Don't worry about this part for now, we'll explore what this means in-depth a little later on.

  2. Method name. We named our method askWhatYouAteFor. Always use method names that make it easy for yourself and others to quickly understand the purpose of a method, and the action(s) it completes.

  3. Parameters. Parentheses will hold any parameters that the method accepts. The argument String meal created a variable named meal that you could use inside of the method. When the method is called, like whatYouAteFor("breakfast"), the parameter, meal, takes on the value of the argument, "breakfast". Similarly, when we call whatYouAteFor("lunch"), the parameter, meal, takes on the value of the argument, "lunch". We, can then use the parameter just like any other variable. Again, this concept in general should be review; but pay attention to the Java-specific components here: Even parameters require you to declare their specific data type in Java!

Calling Methods

Now that we understand all the individual components of our method, let's call it! We'll replace each line of code asking the user what they ate with our new method:

src/main/java/MealTime.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class MealTime {

    public static void main(String[] args) {

        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            askWhatYouAteFor("breakfast");
            String yourBreakfast = bufferedReader.readLine();
            System.out.println("You had " + yourBreakfast + " for breakfast.");

            askWhatYouAteFor("lunch");
            String yourLunch = bufferedReader.readLine();
            System.out.println("You had " + yourLunch + " for lunch.");

            askWhatYouAteFor("dinner");
            String yourDinner = bufferedReader.readLine();
            System.out.println("You had " + yourDinner + " for dinner.");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void askWhatYouAteFor(String meal) {
        System.out.println("What did you eat for " + meal + "?");
    }
}

DRY-ing Code with Custom Methods

However, our code still seems a little longer and more redundant than it needs to be, doesn't it? There are still really similar lines of code being repeated multiple times throughout. Let's see if we can DRY our logic further. We'll relocate even more code into our method:

src/main/java/MealTime.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class MealTime {

   public static void main(String[] args) {

       askWhatYouAteFor("breakfast");
       askWhatYouAteFor("lunch");
       askWhatYouAteFor("dinner");
   }

   public static void askWhatYouAteFor(String meal) {

       try {

           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
           System.out.println("What did you eat for " + meal + "?");
           String yourMeal = bufferedReader.readLine();
           System.out.println("You had " + yourMeal + " for " + meal + ".");
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

Here, we were able to move everything related to each meal into one method call. Now, we aren't repeating ourselves! Consequently, our main method is also much, much easier to read. Can you see the benefit of maintaining DRY code?

Also, note that we moved try/catch block and attached code into our whatYouAteFor method. This is because our bufferedReader and readLine needs to be created inside the method that is going to use it.