C SC 160 Chapter 4: Defining Your Own Classes (basics)
major resource: An Introduction to Object-Oriented Programming with Java, fourth edition, Wu, McGraw Hill, 2006

[ previous | schedule | next ]

Note: most of these topics are also covered in Dr. Pete's No-Nonsense Guide: Writing Java Classes

General Structure of a Class

The ordering of elements within a class definition is flexible, but the textbook author (and I) tend to follow these conventions

A class to inspect: the ClickCounter from our Preliminary Lab

    class ClickCounter {
      public static final int MINIMUM = 0;
      private int maximum;
      private int count;
   
       public ClickCounter(int max) { 
         maximum = max;
         count = MINIMUM; 
      }
   
       public boolean isAtMinimum() { 
         return count == MINIMUM; 
      }
   
       public boolean isAtMaximum() { 
         return count == maximum; 
      }
   
       public int inc() { 
         if (! this.isAtMaximum()) {
            count++; 
         }
         return count;
      }
   
       public void reset() { 
         count = MINIMUM; 
      }
   
       public int getCount() { 
         return count; 
      }
   
       public String toString() {
         return new Integer(count).toString();
      }
   }

Visibility of class members

Class members declared public or private to control visibility. In general, we don't want outsiders messin' with our instance variables directly. We will gladly provide them methods to use however. Thus the general guidelines: The principles behind these guidelines are information hiding and encapsulation.

Information hiding principle: Hide the How! Implementation details, the "how" of a class, should be secret. Advertise the What! Specification details, the "what" of a class, should be published. The "what" means what behaviors the class offers its clients, the "how" means how the behaviors are implemented.

Encapsulation principle: the state of an object should be controlled only by its own methods.

Considerations for writing methods: Constructors

A constructor is a special method which creates objects. Here are some of its characteristics:

Considerations for writing methods : accessor and mutator methods

If you follow visibility guidelines, clients cannot directly access instance variables.

Considerations for writing methods: parameters

Parameters are the means by which client data are given to a method at the moment of invocation. For each parameter defined in a method, the caller (invoker) must provide a corresponding argument.

There are five things you must understand about parameters and arguments

First, you must understand the difference between a parameter and an argument.

Example: A currency converter. The parameter and arguments are highlighted in bold
class CurrencyConverter {
   private double exchangeRate;
   public void setExchangeRate(double rate) { //<--- "double rate" is parameter
      exchangeRate = rate;
   }
   /*******  other methods omitted  *******/
}

public class ConverterClient {
   public static void main(String[] args) {
      double mult = 3.0;
      CurrencyConverter convert = new CurrencyConverter();
      convert.setExchangeRate(2.5); //<--- "2.5" is argument
      convert.setExchangeRate(mult); //<--- "mult" is argument
      convert.setExchangeRate(mult*2+5); //<---"mult*2+5" is argument      
   }
}
Second, you must understand that the argument is an expression evaluated at the time of invocation and the expression value is passed to the method.

Third, you must understand that the parameter name is local to its method, and has no relationship to the argument name (if the argument is a name)

Fourth, you must understand that for parameters of primitive type (int, double, etc.) the mapping from argument to parameter is one-way and the argument cannot be modified by the method

Fifth, you must understand that for parameters of reference type (String, GregorianCalendar, etc.) the mapping from argument to parameter is also one-way, with a twist. The argument cannot be modified by the method, but the object the argument refers to, can be modified by the method, if the object is mutable!

Example: focus your interest on the bolded code below

public class Person {
   private String name;
   public Person() {
      name = "Pat";
   }
   public String getName() {
      return name;
   }
   public void setName(String newName) {
      name = newName;
   }
   /*********  other class members omitted   *********/
}

public class PersonClient {
   public void process(Person who) {
      String name = who.getName();
      name = name.replace('a', 'i'); 
      who.setName(name);
   }
   public static void main(String[] args) {
      PersonClient pc = new PersonClient();
      Person p = new Person();
      p.setName("Sally");
      System.out.println(p.getName());
      pc.process(p);
      System.out.println(p.getName());
   }
}
What is the output produced by main?

Considerations for writing methods : return values

If your method is to respond with a resulting value when invoked, e.g. 2-way communication, then it must follow the rules for return values. Examples are accessor methods and the two examples just above. Example: See the ageAtEndOfCurrentMonth() method just above. It returns an int value.

Example: In the same Person class, you could have an accessor method that simply returns the person's date of birth, as a GregorianCalendar reference.

public class Person {
   private GregorianCalendar dateOfBirth;
   
   public GregorianCalendar getDateOfBirth() {
      return dateOfBirth;
   }
   /******* other class members omitted  *******/
}
It is risky for an accessor method to return an object reference, because the client now has a reference to the person's birth date object and can change it! It would be safer to create a new object for the same date and return a reference to it instead. It would look something like this:
public class Person {
   private GregorianCalendar dateOfBirth;
   
   public GregorianCalendar getDateOfBirth() {
      return new GregorianCalendar(dateOfBirth.get(Calendar.YEAR), 
                                   dateOfBirth.get(Calendar.MONTH),
                                   dateOfBirth.get(Calendar.DATE)  );
   }
   /******* other class members omitted  *******/
}
Let's consider the other side, the responsibilities of the invoker (caller):

Considerations for writing methods : local variables

Methods may use instance variables but this is not always enough. If a method requires additional variables for a calculation or computation, they should be declared inside the method itself. Such variables are called local variables. Example:
  import java.awt.Color;
  public class Square {
     private double height;
     private Color color;
	 
     public double howMuchRed() {
         double area;
         area = height * height;
         return area * color.getRed();
     }
     /********* remainder of class omitted *********/
  }
In this example, the local variable was not really needed since the method body could be written as: return height * height * color.getRed(); For many well-written methods, local variables are not needed.

Here's an example where the need is more obvious:

Example:

import java.util.*;
public class Person {
   private GregorianCalendar dateOfBirth;

   public int ageAtEndOfCurrentMonth() {
      int yearsOld;
      GregorianCalendar today;
      today = new GregorianCalendar();
      yearsOld = today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR);
      if ( today.get(Calendar.MONTH) < dateOfBirth.get(Calendar.MONTH) ) {
          yearsOld--;
      }
      return yearsOld;
   }
   /******* other class members omitted  *******/
}
This method uses two local variables, the primitive variable yearsOld and the reference variable today. It also uses the instance variable dateOfBirth, whose value has been previously set, most likely in a constructor not shown.
[ C SC 160 | Peter Sanderson | Math Sciences server  | Math Sciences home page | Otterbein ]

Last updated:
Peter Sanderson (PSanderson@otterbein.edu)