Class members and methods

The instance method maximum(int 1, int b) returns the larger one of two integer arguments a and b:

Figure 247. Why do we require an instance? Slide presentation
public class Helper {
  // Find the larger of two values
  public int maximum(int a, int b) {
    if (a < b) {
      return b;
    } else {
      return a;
    }
  }
}
final Helper instance = new Helper();

// Why do we need an instance just for
// computing the maximum of two values?
System.out.println("Maximum: " +
            instance.maximum(-3, 5));
Maximum: 5

Observation: The instance's state is irrelevant for finding the maximum of two values.


This common situation leads to another category of methods being independent of any instances. They are being defined on class level by virtue of the static keyword. We call these »class methods« for short:

Figure 248. Solution: Replace instance method by class method using static Slide presentation
public class Helper { 

  static  public int maximum(int a, int b)
   {
    if (a < b) {
      return b; 
    } else {
      return a;
    }
  }
}
// No instance required any longer
System.out.println("Maximum: " +
     Helper.maximum(-3, 5)); 
Maximum: 5

Selecting method maximum(int a, int b) from class Helper.

Choosing class scope over instance scope.


Figure 249. Club membership objectives Slide presentation
  • Each club member has got a name.

  • Each member has got an ascending unique membership number.

  • The overall club's member count needs to be accounted for.

Solution:

  • Class level: Advance club's member count by each new member.

  • Instance level: New members receive name and current member count plus 1.


Figure 250. Step 1: Implementing club member names. Slide presentation
public class ClubMember {

  final private String name;

  public ClubMember(final String name) {
    this.name = name;
  }
  public String toString() {
    return "Member " + name;
  }
}

Figure 251. Showing membership info. Slide presentation
final ClubMember
   john = new ClubMember("John"),
  karen = new ClubMember("Karen");

System.out.println(john.toString());
System.out.println(karen.toString());
Member John
Member Karen

Figure 252. Step 2: Adding membership numbers. Slide presentation
public class ClubMember {

  static  private int memberCount = 0;

  final private int memberNumber; 
  final private String name;

  public ClubMember(final String name) {
    this.name = name;
    memberNumber = ++memberCount; 
  }
  public String toString() {
    return "Member " + name + ", member number " +  memberNumber ;
  }
}

The keyword static defines memberCount as a class variable keeping track of the club's overall member count.

This is similar to our Helper.max(int a, int b) method in Figure 248, “Solution: Replace instance method by class method using static being also defined on class level rather than instance level.

memberNumber and name being defined as instance variables: In contrast to memberCount each individual club member does have both its individual name and membership number.

On creating a new instance of ClubMember two actions happen in succession:

  1. The overall memberCount variable is being incremented by one.

  2. The new club's overall member count is being assigned to the current instance.

Accessing an individual member's name and membership number.


Figure 253. Showing membership numbers. Slide presentation
final ClubMember
   john = new ClubMember("John"),
  karen = new ClubMember("Karen");

System.out.println(john);  // toString() is being
System.out.println(karen);   // called implicitly 
Member John, member number 1
Member Karen, member number 2

As an aside: At runtime the last two lines are strictly equivalent to:

...
System.out.println(john.toString());
System.out.println(karen.toString()); 

This is due to the chosen toString() method signature as we will learn in the section called “Overriding toString()”.


Figure 254. Member creation steps Slide presentation

Figure 255. Accessing the club's overall member count? Slide presentation
public class ClubMember {

  static private int memberCount = 0;
  ...
  public ClubMember(final String name) {
    this.name = name;
    memberNumber = ++memberCount;
  }
  static public int getMemberCount()  {
    return memberCount; 
  }
  ...
}

getMemberCount() being defined as class method by virtue of the static keyword.

Class (static) methods can only access class (static) variables.


Figure 256. Accessing the club's member count Slide presentation
final ClubMember
   john = new ClubMember("John"),
  karen = new ClubMember("Karen"),
  petra = new ClubMember("Petra");

System.out.println(karen.toString());

System.out.println("Club's member count:"
   + ClubMember.getMemberCount());
// Good: Prevent tampering memberCount
// variable.
Member Karen, member number 2
Club's member count:3

Figure 257. Syntax accessing class members Slide presentation
Class Variable

{class name}.{variableName}

Class Method

{class name}.{methodName}([parameter])


Figure 258. static / non-static wrap up Slide presentation
public class X {
         int a; 
  static int b; 

Variable a defined once per instance of class X.

Variable b defined once per class X.


Figure 259. Finally understanding System.out.print(ln) Slide presentation
          
System.out.print(...)

Class System in package java.lang.

Class variable (static) out of type PrintStream in class System

One of 9 overloaded methods in class PrintStream.


exercise No. 98

Class vs. instance

Q:

  1. Is it possible to implement toString() from Figure 252, “Step 2: Adding membership numbers. ” as a class method by adding static?

  2. Is it possible to remove the static modifier from getMemberCount() in Figure 255, “Accessing the club's overall member count? ”?

A:

  1. Adding static yields:

    public class ClubMember {
     ...
      final private int memberNumber; // Instance variable
      final private String name;      // Instance variable
    ...
      static public String toString() { // Class method
        return "Member " + name + ", member number " + memberNumber;
      }
    }

    The above code does not compile: The two variables name and memberNumber are instance variables. Trying to access these from a static context results in two compile time error messages:

    model/ClubMember.java:20: error: non-static variable name cannot
      be referenced from a static context
        return "Member " + name + ", member number " + memberNumber;
                           ^
    model/ClubMember.java:20: error: non-static variable memberNumber
      cannot be referenced from a static context
        return "Member " + name + ", member number " + memberNumber;
                                                       ^
    2 errors

    Each member is being defined by its name and member number. static methods are being defined on class rather than on instance level. According to Class Method execution happens by calling ClubMember.toString(). There is thus no instance involved and consequently both instance variables name and memberNumber do not exist in any static context.

  2. Defining getMemberCount() as an instance rather than a class method is possible albeit discouraged:

    public class ClubMember {
    
      static private int memberCount = 0;
     ...
      public int getMemberCount() {
        return memberCount;
      }
     ...
    }

    Instance methods may access class variables. However calling the method now requires an instance:

    final ClubMember
       john = new ClubMember("John"),
      karen = new ClubMember("Karen"),
      petra = new ClubMember("Petra");
    
    System.out.println("Club's headcount:" + john.getMemberCount());

    This is actually a regression with respect to the static class method variant since the number of club members in fact does not depend on any individual member instance.

    We may reveal this changes futility by:

    final ClubMember
       john = new ClubMember("John"),
      karen = new ClubMember("Karen"),
      petra = new ClubMember("Petra");
    
    System.out.println("Club's headcount:" + john.getMemberCount());
    System.out.println("Club's headcount:" + karen.getMemberCount());
    System.out.println("Club's headcount:" + petra.getMemberCount());

    All three calls to getMemberCount() will yield he same value 3 referring to a total amount of three club members being created so far.

    Even worse: Without creating an instance of ClubMember we can no longer access the club's overall member count. But even without any member instance we still have a valid member count of zero.

exercise No. 99

Distinguishing leap- and non-leap years

Q:

In Leap years you already started a leap year related exercise. This exercise is about wrapping your implementation into a method:

/**
 * Characterizing a given year either as leap year or
 * non- leap year
 *
 * @param year The year in question.
 * @return true if the year parameter is a leap year, false otherwise.
 */
public static boolean isLeapYear(int year) {
     ...
}

Write unit tests prior to actually implementing boolean isLeapYear(int year)!

After finishing you should also be able to test your implementation manually:

public static void main(String[] args) {
  System.out.println("Is 1800 a leap year? " + isLeapYear(1800));
  System.out.println("Is 2000 a leap year? " + isLeapYear(2000));
  System.out.println("Is 2016 a leap year? " + isLeapYear(2016));
}

This should produce the following output:

Is 1800 a leap year? false
Is 2000 a leap year? true
Is 2016 a leap year? true

A:

We start by a dummy implementation:

public static boolean isLeapYear(int year) {
  return true; // Wrong in most cases, we yet don't care.
}

This enables us writing unit tests beforehand:

/**
 * Testing leap year behaviour.
 */
public class LeapYearTest {
   /**
    * Test for leap years
    */
   @Test
   public void testLeapYearPositive() {
      assertTrue(LeapYear.isLeapYear(2000));
      assertTrue(LeapYear.isLeapYear(1984));
   }

   /**
    * Test for non-leap years
    */
   @Test
   public void testLeapYearNegative() {
      assertFalse(LeapYear.isLeapYear(2001));
      assertFalse(LeapYear.isLeapYear(1800));
   }
}

Obviously all negative tests will fail. We now implement the desired method:

public static boolean isLeapYear(int year) {
  if (year % 400 == 0) {                    // Every 400 years we do have a leap year.
    return true;
   } else if (year % 4 == 0 &&  0 != year % 100) { // Every 4 years we do have a leap
     return true;                                  // year unless the year in
                                                   // question is a multiple of 100.
   } else {
     return false;
   }
}

This one is easy to read. Experienced programmers however prefer compact code:

public static boolean isLeapYear(int year) {
  return
    year % 400 == 0 ||                  // Every 400 years we do have a leap year.
    year % 4 == 0 &&  0 != year % 100;  // Every 4 years we do have a leap year
                                        // unless the year in question ...
}

exercise No. 100

A method for printing square numbers using for, while and do ... while

Q:

You already did exercises on printing mathematical tables. This exercise is about decomposing tasks into methods thereby improving code readability. We also explore using different loop types.

Consider the following example:

public class LoopExample {

  public static void main(String [] args) {
    squareNumbers(14);
  }

  /**
   * Compute all square numbers starting from 4 in steps of 3 being smaller
   * than a given limit. The results are being written to standard output.
   * Implemented by a for -loop.
   *
   * @param limit An upper exclusive bound for the highest number to be squared..
   */
  public static void squareNumbers(final int limit) {
    System.out.println("Computing square numbers");
    for (int i = 4; i < limit; i += 3) {
      System.out.println("i = " + i + ", i * i = " + i * i);
    }
    System.out.println("Finished computing square numbers");
  }
}

Re-implement the above code in two different ways:

  1. while (...) {...} loop:

      ...
    public static void whileSquareNumbers(final int limit) {
      System.out.println("Computing square numbers");
    ...
      while (...) {
    ...
      }
      System.out.println("Finished computing square numbers");
    } ...
  2. do {...} while(...) loop:

      ...
    public static void doWhileSquareNumbers(final int limit) {
      System.out.println("Computing square numbers");
    ...
      do {
       ...
      } while(...);
    
      System.out.println("Finished computing square numbers");
    } ...

Caveat: The do {...} while(...) part is a little bit tricky. Read the method's documentation thoroughly to avoid a common pitfall.

Which of these three loop implementations is your favourite? Give a reason.

A:

The while loop is actually quite straightforward to implement:

public class LoopExample {
  ...
  public static void whileSquareNumbers(final int limit) {
    System.out.println("Computing square numbers");
    int i = 4;
    while (i < limit) {
      System.out.println("i = " + i + ", i * i = " + i * i);
      i += 3;
    }
    System.out.println("Finished computing square numbers");
  } ...

Its tempting to implement a do ... while in a similar fashion:

public class LoopExample {
  ...
  public static void doWhileSquareNumbers(final int limit) {
    System.out.println("Computing square numbers");
    int i = 4;
    do {
      System.out.println("i = " + i + ", i * i = " + i * i);
      i += 3;
    } while (i < limit);
    System.out.println("Finished computing square numbers");
  } ...

This implementation is however flawed: If we call doWhileSquareNumbers(3) we still receive one line of output. But according to the documentation no output is to be expected. Whatever the argument is, at least one line of output is being printed contradicting the method's Javadoc. Avoiding this error requires the loop to be enclosed in an if- statement:

public static void doWhileSquareNumbers(final int limit){
  System.out.println("Computing square numbers");
  int i = 4;
  if (i < limit) { // Needed !!!
    do {
      System.out.println("i = " + i + ", i * i = " + i * i);
      i += 3;
    } while (i < limit);
  }
  System.out.println("Finished computing square numbers");
}

This if-clause reminds us that a do {...} while (...) is an ill-suited choice here: Both a while(...){...} or a for(...){...} loop avoid this problem in the first place rather than requiring its symptoms to be cured.

Actually a for(...){...} loop is our best choice here since:

  1. The number of iterations is known in advance.

  2. The increment is constant.

  3. It allows for prior initialization.

.