RU beehive logo ITEC dept promo banner
ITEC 120
2008spring
ibarland,
jdymacek

homeinfoexamslectureslabshws
RecipeLawsliessyntaxjava.lang docsjava.util docs

lect09b
static methods

Recall the first methods we wrote, which were part of class PizzaServer: the method pizzaArea, as well as methods like crustArea and wedPizzaArea. Those methods were a bit odd, because it didn't matter which PizzaServer was asked; they all gave the same answer. That is, the answer didn't depend on the PizzaServer's state. We could make a million PizzaServers, and they'd all ask the same thing. (In fact, we might want that question answered even before any PizzaServers are created.)

Actually, it is possible to write methods which aren't tied to a particular instance.

A static method is a method which is not called through an individual instance. Instead, it is associated only with a class (but no instances of that class). This means you can call that method even if an instance has never been created.
Really, pizzaArea should have been a static method:
class PizzaServer {
  public  double MINIMUM_WAGE = 5.75;
  private double salary = MINIMUM_WAGE;
  private double balance = 0;
  private String almaMater;

  // ...Constructors, setters omitted


  public static double pizzaArea( double diam ) {
    double radius = diam/2;
    return 3.14 * radius * radius );
    }

  }
We can call these methods in BlueJ, by right-clicking directly on the class, even though we haven't created any PizzaServer instances! Notice which methods are shown when you right-click on the class, and (after you do create an instance) which methods are shown when you right-click on the instance.

Then, from the code pad, we can now write:

PizzaServer.pizzaArea(12)
PizzaServer.pizzaArea(1)
PizzaServer.pizzaArea(0)
Think of static methods as being methods where you walk up to the PizzaServer-factory, not a particular PizzaServer.

Or, consider Math.sqrt. It's natural question to ask what the square root of some number is; but it's odd to say you must ask some particular Math object. I'd lied earlier; there is no Math object (we never called new, so there couldn't have been an object!). Instead, Math is just a class chock full of static methods. It's called a “utility class” — you never make a new math object, you only call static methods.

Or, consider: when writing Explorer methods, we really wish we had a method createNewLint(). Clearly, such a method would belong in class Treasure. But: it seems odd that you walk up to one Treasure (say, a chocolate egg) and ask it to create a Lint for you.
Really, we want a method which we can call even if no (other) Treasures have been created yet.

warning: The Java keyword “static” means “associated with the class, not a particular instance”. It does not mean “unchanging”. (It also does not mean that you will get zapped if you touch it, so there is no reason to be nervous about static methods.)

Limitations of static methods

You cannot mention this in a static method.
This means you can't call (non-static) methods from inside a static method: you can't say this.getSalary(). (This further means that you can't even write getSalary(), because java just rewrites that as java.getSalary() behind your back.)

If you see the error message “cannot call a non-static method from a static context”, it means you're using this inside a static method. If you get this message, stop and think: Is the method you're writing really supposed to be static? If so, only then wonder about the method you're calling -- if it wasn't static, should it have been?
Example:

  // ERROR:
  // non-static method getBalance cannot be referenced from a static context.
  static boolean inDebt() {
    return (getBalance() < 0);
    }
What is the solution here?

Note that you might have object-references that were passed in as arguments; you can certainly invoke methods on those parameters:

  static PizzaServer theRicherOf( PizzaServer p1, PizzaServer p2 ) {
    if (p1.getBalance() < p2.getBalance()) {
      return p1;
      }
    else {
      return p2;
      }
    }
What you cannot do from a static method is to write “getBalance()” since Java rewrites it as this.getBalance() which then triggers “cannot access method from a static context”.
  // ERROR:
  // non-static method getBalance cannot be referenced from a static context.
  static PizzaServer theRicherOf( PizzaServer p1, PizzaServer p2 ) {
    if (p1.getBalance() < getBalance()) {  //  an implicit `this`
      return p1;
      }
    else {
      return p2;
      }
    }

Remember, the solution to the error message “cannot access method/field from a static context” is probably not making that other method or field static. Although that gets rid of the error message, it's almost certainly not what you to do, if the enclosing method (“context”) really was meant to be static. The proper solution is to think:

Discuss: functions for converting between int and double, from lect04c—bad arithmetic:

(new Double(2.718)).intValue()
vs.
Double.parseDouble( Integer.toString(45) );

Optional: calling methods from constructors

We currently have tension between a law of programming, and a rule-of-thumb:

Here's an example of tension between these two:
class PizzaServer() {
  /** The wage, in $/hr. */
  private double salary;

  /** The minimum wage (in $/hr). */
  public static double MINIMUM_WAGE = 5.75;

  /** Constructor.
   * @param The initial salary (in $/hr)
   */
  PizzaServer( double _startSalary ) {
    if (_salary < MINIMUM_WAGE) {
      this.salary = MINIMUM_WAGE;
      }
    else {
      this.salary = _salary;
      }
    }


  /** Set this PizzaServer's salary (subject to federal minimum wage).
   * @param _salary The new salary rate.
   *   If _salary is below the min.wage ({@value MINIMUM_WAGE}), we use that instead.
   */
  void setSalary( double _salary ) {
    if1 (_salary < MINIMUM_WAGE) {
      this.salary = MINIMUM_WAGE;
      }
    else {
      this.salary = _salary;
      }
    }

  }
Do you recall the rationale for not wanting to call methods from a constructor? Nothing inherently wrong, except that writing (or modifying) a method, it's deeply ingrained to assume that the object is in a valid state (fully initiailized). But when called from a constructor, the object is still partway through being initialized; bugs can be easy to introduce and very difficult to locate. But this restriction doesn't apply to static methods, since there is no object involved with a static method. So it's fine to call a static method from a constructor.

Thus we have an easy solution to the above tension: write static methods to do the work which you want to call from a constructor as well as other places:

  /** Return the 
   * @param The proposed salary.
   */
  static double legalSalary( double _salary ) {
    if (_salary < MINIMUM_WAGE) {
      return MINIMUM_WAGE;
      }
    else {
      return _salary;
      }
    }

  /** Constructor.
   * @param The initial salary (in $/hr)
   */
  PizzaServer( double _startSalary ) {
    this.startSalary = PizzaServer.legalSalary( _startSalary );
    }


  /** Set this PizzaServer's salary (subject to federal minimum wage).
   * @param _salary The new salary rate.
   *   If _salary is below the MINIMUM_WAGE, we use that instead.
   */
  void setSalary( double _salary ) {
    this.salary = PizzaServer.legalSalary( _salary );
    }

Test drivers (optional (for now?))

Recall from lab08b—printing: Pair O' Dice: the last method you wrote was a method which rolled a pair and printed the result to the screen, and then did it again. This was meant purely for testing (as long as you're willing to pore over the results on the console and check everything looks correct).

class PairODice {
  // ...

  /** Test one of our objects by rolling it a few times;
   *  see the console window after calling this method, to check 
   *  that things look okay.
   */
  void makeTestRolls() {
    this.roll();
    System.out.println( this.toString() );
    this.roll();
    System.out.println( this.toString() );
    }
  }

With static methods, we can go one better: we can make a static method which creates several PairODice objects, rolls them, and prints them. (This is almost as good as unit testing!)

class PairODice {
  // ...

  /** A test driver:
  *   Test one of our objects by rolling it a few times;
   *  see the console window after calling this method, to check 
   *  that things look okay.
   */
  public static void test() {
    PairODice p1;
    PairODice p2;
    p1 = new PairODice();
    p2 = new PairODice();

    p1.roll();
    System.out.println( p1.toString() );
    p1.roll();
    System.out.println( p1.toString() );

    p2.roll();
    System.out.println( p2.toString() );
    p2.roll();
    System.out.println( p2.toString() );
    }
  }
Now we can call this static method, and let it worry about making several instance. Usually, test-drivers are much inferior to unit-testing: We have to manually inspect the console window for correctness! (After all, this test driver doesn't know what to assert as the correct answer.) However, here are some advantages of a test driver that prints to the console:


1This if-statement could expressed more concisely using Java's conditional operator: this.salary = (_salary < MINIMUM_WAGE) ? MINIMUM_WAGE : _salary;. Actually, computing the larger of two numbers is so common there is even a built-in method which will do this for us: this.salary = Math.max( _salary, MINIMUM_WAGE );.      

2Or as alluded to in the precding point, you can think about more complicated tests.      

homeinfoexamslectureslabshws
RecipeLawsliessyntaxjava.lang docsjava.util docs


©2008, Ian Barland, Radford University
Last modified 2008.Mar.19 (Wed)
Please mail any suggestions
(incl. typos, broken links)
to iba�rlandrad�ford.edu
Powered by PLT Scheme