๐Ÿ““3.5: Writing Methods

Table of Contents


๐Ÿ“– This page is a condensed version of CSAwesome Topic 3.5


Writing Methods in Object Classes

In object-oriented programming, the three main parts of a class are:

  1. ๐Ÿง  Instance Variables which hold data/attributes/values associated with each object,
  2. ๐Ÿ—๏ธ Constructors whose job is to initialize (assign values) for the instance variables,
  3. ๐Ÿƒโ€โ™‚๏ธ Methods which contain code for the behaviors/actions/processes of an object, which can use the instance variables defined in the class.

In Unit 1, we created Turtle objects and used methods like forward() which changed the x and y coordinates (instance variables) of the turtle. We also defined generic static methods that did not need specific objects to work. In this unit, we will learn how to write our own methods in our own classes.

What are Methods?

Method
A reusable block of code that performs a specific task.

Methods are defined inside a class, and have direct access the private instance variables in that same class. Methods themselves are usually public so others can call (use/run) them elsewhere, such as in another class/program/Java file.

For example, the print() method below prints the instance variables of the Person object, p:

    public class Person {
        private String name;
        private String email;

        // Method DEFINITION
        public void print() {
            System.out.println("Name: " + name);
            System.out.println("Email: " + email);
        }

        public static void main(String[] args) {
            Person p = new Person();
            p.print();  // Method CALL
        }
    }

๐Ÿ“ How to Define a Method: Write the methodโ€™s HEADER (like a โ€œlabelโ€ or โ€œoverviewโ€) and BODY (the actual โ€œprocessโ€ or โ€œstepsโ€) code in the objectโ€™s template/blueprint class.

  // method HEADER/SIGNATURE
  public void methodName()
  {
      // method BODY code in curly brackets
  }

In the past units, this has already been done for you by another programmer!

Void vs. Non-Void Methods

Void Method
A method that does not return a value. It is used when you want to perform an action but do not need to output data.

๐Ÿ“ The method header for a void method is written like this:

public void methodName()
{
  // method body
}

The print() method shown earlier is a void method. It does not return a value, but it does โ€œdo somethingโ€: it prints out the name and email of the person.

Non-Void Method
A method that returns a single value. Its body must have a return statement, usually at the end, that returns either a variable's value or an expression. Its header includes the return type (the type of the value in the return statement) in place of the keyword void.

๐Ÿ“ The method header for a non-void method is written like this:

public returnType methodName()
{
  // method body
  ...
  return valueOrExpression;
}

In non-void methods, a return expression (compatible with the return type) is evaluated, and the resulting value is returned. This is referred to as a โ€œreturn by valueโ€ technique.

The return keyword is used to return the flow of control to the point where the method or constructor was called. Any code that is sequentially after a return statement will never be executed, so usually non-void methods end with a return statement. Executing a return statement inside a selection or iteration statement will halt the statement and exit the method or constructor.

In Unit 1, we used static non-void methods like Math.random() which returned a random number. We also used non-void methods that belonged to Turtle objects like getXPos() and getYPos() that returned the x and y coordinates of the turtle.

The most common non-void methods in Java are methods that start with get and return the value of an instance variable. We will learn about these in the next section.

๐Ÿšจ Some common errors when writing and using non-void methods are:

  • Forgetting a return type like int before the method name.

  • Forgetting to use the return keyword to return a value at the end of the method.

  • Returning too soon from a method. If you have a return statement in a selection or iteration statement, the method will exit at that point and not execute the rest of the code.

  • Forgetting to do something with the value returned from a method, like assigning it to a variable or printing it out.


Accessor Methods (Getters)

Since the instance variables in a class are usually marked as private to the class, if you want code outside the class to be able to access the value of an instance variable, you need to write what is formally called an accessor methods but which everyone actually just calls a getter.

How to Define a Getter

๐Ÿ‘‰ A getter is a public method that takes no arguments and returns the value of the private instance variable.

// Instance variable declaration
private typeOfVar varName;
...
// Getter method template
public typeOfVar getVarName() {
  return varName;
}

Notice that the getterโ€™s return type is the same as the type of the instance variable, and all the body of the getter does is return the value of the variable using a return statement.

Hereโ€™s an example of an accessor method called getName for the Student class which also demonstrates how to call getName using a Student object:

public class Student {
  // Instance variable name
  private String name;

  /** getName() example
  *  @return name */
  public String getName() {
    return name;
  }

  public static void main(String[] args) {
    // To call a get method, use objectName.getVarName()
    Student s = new Student();
    System.out.println("Name: " + s.getName() );
  }
}

Note that getters only return the value of the variable. In other words, the code that called the getter and which receives that value has no ability to change the objectโ€™s instance variable - they just get a copy of the value.

However if the instance variable is a reference (object) type like String orPerson the value that is copied is the value of the reference. That means the caller receives a new copy of the reference that points to the same object as is stored in the instance variable.

In the next section, when we talk about mutation, youโ€™ll see how that means that the caller might be able to change the object even though it canโ€™t change the reference.

Some common errors when writing and using getters are:

  • Forgetting a return type like int before the method name.
  • Forgetting to use the return keyword to return a value at the end of the method.
  • Forgetting to do something with the value returned from a method, like assigning it to a variable or printing it out.

The toString Method

๐Ÿ“œ While not strictly speaking a getter, another important method that returns a value is the toString method. This method is called automatically by Java in a number of situations when it needs to convert an object to a String.

Most notably the methods System.out.print and System.out.println use an objectโ€™s toString method to convert a object argument into a String to be printed.

Here is the Student class again, but this time with a toString method:

  public class Student {
      private String name;
      private String email;
      private int id;

      public Student(String initName, String initEmail, int initId) {
          name = initName;
          email = initEmail;
          id = initId;
      }

      // toString() method
      public String toString() {
          return id + ": " + name + ", " + email;
      }
  }

Note that when we call System.out.println(s1) in the tester class below, it will automatically call the toString method within the Student class to get a String representation of the Student object:

  public class TesterClass {
      // main method for testing
      public static void main(String[] args) {
          Student s1 = new Student("Skyler", "skyler@sky.com", 123456);
          System.out.println(s1);
      }
  }

The toString method will return a String that is then printed out.


Mutator Methods (Setters)

As we saw in the last section, since we typically make instance variables private, we have to define getters if we want to allow code outside the class to access the value of particular instance variables. By the same token, if we want to allow code outside the class to change the value of an instance variable we have to provide what is formally called a mutator method but which everyone actually calls a setter.

A setter is a void method with a name that starts with set and that takes a single argument (of the same type as the instance variable) to be set. The *effect** of a setter is to assign the new provided value to the instance variable.

โš ๏ธ Not all instance variables are meant to be manipulated directly by code outside the class!

For example, consider the Turtle class. It provides getters getXPos and getYPos but it does not provide corresponding setters. There are, however, methods that change a Turtle\ โ€™s position like forward and moveTo. But they do more than just changing the values of instance variables; they also take care of drawing lines on the screen if the pen is down.

How to Define a Setter

๐Ÿช„ A setter is a void method with a name that starts with set and that takes a single argument of the same type as the instance variable to be set.

// Instance variable declaration
private typeOfVar varName;
...

// Setter method template
public void setVarName(typeOfVar newValue) {
  varName = newValue;
}

The effect of a setter is to assign the new provided value to the objectโ€™s instance variable.

Hereโ€™s an example of the Student class with a setter for the name variable:

public class Student {
     // Instance variable name
     private String name;

     /**
     * setName sets name to newName
     * @param newName
     */
     public void setName(String newName) {
          name = newName;
     }

     public static void main(String[] args) {
          // To call a set method, use objectName.setVar(newValue)
          Student s = new Student();
          s.setName("Ayanna");
     }
}

Compare the difference between setters and getters in the following figure:

image

  • Getters return an instance variableโ€™s current value, have the same return type as this variable, and accept NO parameters.
  • Setters have a void return type and accept a new value as a parameter to change the value of the instance variable.

Calling Instance Methods

There are three steps to writing & using custom instance methods:

  1. ๐Ÿ“ Method Definition: Write the methodโ€™s HEADER (like a โ€œlabelโ€ or โ€œoverviewโ€) and BODY (the actual โ€œprocessโ€ or โ€œstepsโ€) code in the objectโ€™s template/blueprint class.
      // Step 1: DEFINE the method in the template class
      public void methodName()
      {
       ...
      }
    
  2. ๐Ÿ—๏ธ Object Construction: Create an object instance of that class type in a main() method, either in the objectโ€™s class or in another class file (like Main.java).
      // Step 2: CREATE an object in main or from outside the class
      ClassName objectName = new ClassName();
    
  3. ๐Ÿ“ฃ Method Call: Whenever you want to use the method, call it using the DOT OPERATOR.
      // Step 3: CALL the object's method
      objectName.methodName(); 
    

    NOTE: There are different ways to call methods, depending on some details (see below).

The choices a programmer makes when defining a method determine how the method gets implemented by users in practice. The following flowchart can be used to compare three different ways of calling methods:

image

  • Class methods (static methods, see lesson ๐Ÿ““ 3.7) are not specific to object instances, so they are called using the class name itself.
  • Instance methods (topic of this lesson) are called on a specific object of the class.
    • If you are calling the instance method from the main() method or from another class, you must first create an object instance of that class and then call its methods using object.methodName().

      Most common scenario in this course!

    • If you are calling the method from within the same class, you can just call the method using methodName() which will refer to the current object.

      This scenario is like having different methods โ€œtalk to each otherโ€ within the class, like helping each other work.

Parameters & Arguments

A parameter is a placeholder variable in a methodโ€™s header that is used to pass in data that the method needs to do its job. In a setter, the parameter is the new value that you want to assign to the instance variable. Methods with parameters receive values through those parameters and use those values in accomplishing the methodโ€™s task.

An argument is an actual value that is passed into a method when the method is called. It is saved into a parameter variable.

image

๐Ÿคตโ€โ™€๏ธ๐Ÿคต๐Ÿคตโ€โ™‚๏ธ When you define your own method, the variables you specify for it in the method header are called formal parameters.

๐Ÿ“ฃ When you call the method to do its job, you give or pass in arguments or actual parameters to it that are then saved in these local parameter variables.

When a method is called, the right method definition is found by checking the method signature or header at the top of the method definition to match the following:

  1. The method name
  2. The number of arguments
  3. The data types for the arguments
  4. The return type (either void or a data type like int, String, etc.)

The arguments passed to a method must be compatible in number and order with the types identified in the parameter list of the method signature. When calling methods, arguments are passed using call by value. Call by value initializes the parameters with copies of the arguments.

When an argument is a primitive value, the parameter is initialized with a copy of that value. Changes to the parameter have no effect on the corresponding argument.


โญ๏ธ Summary

  • (AP 3.5.A.1) A void method does not return a value. Its header contains the keyword void before the method name.
  • (AP 3.5.A.2) A non-void method returns a single value. Its header includes the return type in place of the keyword void.

  • (AP 3.5.A.3) In non-void methods, a return expression compatible with the return type is evaluated, and the value is returned. This is referred to as return by value.
  • (AP 3.5.A.4) The return keyword is used to return the flow of control to the point where the method or constructor was called. Any code that is sequentially after a return statement will never be executed. Executing a return statement inside a selection or iteration statement will halt the statement and exit the method or constructor.
  • (AP 3.5.A.5) An accessor method (getter) allows objects of other classes to obtain a copy of the value of instance variables or class variables. An accessor method is a non-void method.
  • (AP 3.5.A.6) A mutator (modifier) method (setter) is a method that changes the values of the instance variables or class variables. A mutator method is often a void method.
  • Comparison of accessor/getters and mutator/setters syntax:

image

  • (AP 3.5.A.7) Methods with parameters receive values through those parameters and use those values in accomplishing the methodโ€™s task.
  • (AP 3.5.A.8) When an argument is a primitive value, the parameter is initialized with a copy of that value. Changes to the parameter have no effect on the corresponding argument.

  • The toString method is an overridden method that is included in classes to provide a text description of a specific object. It generally includes what values are stored in the instance data of the object. If System.out.print or System.out.println is passed an object, that objectโ€™s toString method is called, and the returned String is printed. An objectโ€™s toString method is also used to get the String representation when concatenating the object to a String with the + operator.

Acknowledgement

Content on this page is adapted from Runestone Academy - Barb Ericson, Beryl Hoffman, Peter Seibel.