Lesson Weekend

Before we get started with some hands-on web development with Java, let’s take the time to clarify a couple of important Java concepts before we move on. As mentioned in the prework for week one, we will be discussing general programming related topics as well teaching you applicable skills as part of this course on Java. This is slightly more complex stuff, so give this a proper readthrough!

Imagine that we created a data model (AKA class definition) that looked like this:

Rectangle.java
public class Rectangle {
  private int length;
  private int width;

  public Rectangle(int length, int width) {
    this.length = length;
    this.width = width;
  }

  public int getLength() {
    return length;
  }

  public int getWidth() {
    return width;
  }

  public boolean isSquare() {
    return length == width;
  }

  public int area() {
    return length * width;
  }  
}

This simple class definition with two properties that we get from our user and then use to make a simple object through a constructor. We also have two getters to retrieve information about our instantiated objects, and two methods that we can call to get information about our object. One returns a boolean, one returns an int.

Note: If the above is feeling confusing, please stop, and revisit relevant lessons on objects and encapsulation from week 1 before you proceed.

The above is called a POJO in Java slang. This stands for Plain Old Java Object. As you get more experience in writing Java, writing POJOs will become second nature.

Everything contained in the class is either an attribute about an instance of the class, or a method to do something with an instance. For example, getLength() returns a Rectangle's private length attribute, and isSquare() evaluates the Rectangle's length and width properties to determine if it is also a square.

Essentially, all code here revolves around individual instances, or objects of the class. This has been the case with all classes we've created thus far. So far, so good. Cool.

Handling Class-Wide Information

Consider the following: Let's pretend we’re a nerd, and want to know how many Toyota Camry cars have been produced. Would we seek out this information by examining an individual instance of a Toyota Camry? Or would we attempt to gather this information by asking the factory where these vehicles are manufactured?

You'd ask the manufacturer, right? After all, the red Toyota Camry down the street doesn't "know" how many other Camrys have been manufactured. But the factory responsible for creating them most likely does.

Static Methods and Variables

Now, let's consider the Rectangle class from the example above. Pretend we’re an even bigger nerd and need to know how many Rectangle objects have been created. Do we ask an individual instance of the Rectangle class? Or do we ask the class itself?

Similar to the Toyota Camry example above, it makes far more sense to ask the class itself. Just like the Toyota factory, it is the entity responsible for manufacturing each of these objects. One individual Rectangle doesn't "know" how many other Rectangles have been constructed; it only knows its own length and width properties.

Methods and variables that store or manipulate information about an entire class are declared static. Static means that instead of referring to or interacting with an individual instance/object, they interact with/refer to with the class as a whole. It’s an important distinction.

To revisit our metaphor, static methods and variables are similar to the Toyota factory. They have access to information about each individual object that factory creates.

Non-static methods and variables (every method and variable we've written so far) are like the specific instance of a red Toyota Camry. While it was created at the factory, it only has access to its own properties. It doesn't know about all the other Camrys ever produced.

Creating and Using Static Methods and Variables

Declaring Static Variables

We could declare a static property that maintains a list of all Rectangle objects we create like this:

rectangle/src/main/java/Rectangle.java
import java.util.List;
import java.util.ArrayList;

public class Rectangle {
  private int length;
  private int width;
  private static List<Rectangle> instances = new ArrayList<Rectangle>();
...

As you can see, we've added a new property to the Rectangle class called instances.

  • It's an ArrayList that will eventually contain all Rectangle objects created by this class.
  • We've declared it private in order to follow best practices of encapsulation.
  • We have also declared it static because the information it holds is not about a singular Rectangle, but related to the class as a whole.

Similar to other class attributes, we must include a corresponding public getter method to retrieve the information stored in instances. But before we do that we'll write a test.

Testing Static Methods

rectangle/src/test/java/RectangleTest.java
...
@Test
public void all_returnsAllInstancesOfRectangle_true() throws Exception {
  Rectangle firstRectangle = new Rectangle(10, 20);
  Rectangle secondRectangle = new Rectangle(5, 5);
  assertTrue(Rectangle.all().contains(firstRectangle));
  assertTrue(Rectangle.all().contains(secondRectangle));
}
...

A few things to note regarding the test above:

  • First, you may notice the last two lines look different then assertions we've written previously. There are many ways to construct our JUnit assert statements. assertTrue(Rectangle.all().contains(firstRectangle)); is simply asserting that the statement we pass to it returns true. This is the same as writing: assertEquals(true, Rectangle.all().contains(firstRectangle));

  • We use two assertions here because we want to ensure that the ArrayList returned by Rectangle.all() includes both test rectangles, so we write two lines to check for each one individually.

Adding Content to Static Variables

Next, we now need to add every Rectangle object we create to the instances ArrayList. But where is the best place to do this? Because we want to include every new Rectangle in instances, the logic for adding them to the ArrayList must run every time we create a Rectangle, without fail.

What method is always called when we create instances of a class? The constructor! We'll add the following code to our Rectangle constructor:

rectangle/src/main/java/Rectangle.java
public class Rectangle {
  private int length;
  private int width;
  private static List<Rectangle> instances = new ArrayList<Rectangle>(); //all Rectangle types “share” this list

  public Rectangle(int length, int width) {
    length = length;
    width = width;
    instances.add(this); // automatically adds itself on creation!
  }
...

When we are working inside of the constructor, we can use the keyword this to reference that object. This is similar to the manner we used this in JavaScript during Intro to Programming. So, each time we call our constructor method, it constructs a new Rectangle. Now, it also adds this new Rectangle to instances.

Retrieving Static Variables

Next, we can define the corresponding getter method to retrieve theinstances list:

rectangle/src/main/java/Rectangle.java
...
public static List<Rectangle> all() {
  return instances; // this is all rectangles, ever!
}
...

Like any getter method, it returns the private instances attribute. However, unlike other getter methods, it is declared static. Both variables and methods that deal with entire classes must be declared static.

After adding the code above, our test passes.

Calling Static Methods

Additionally, because the method we've just written is static it must be called on the class itself and not a particular instance. We saw this in the JUnit test we wrote above. Calling a method directly on a class looks like this:

Rectangle.all() // note the uppercase. Calling method on CLASS ITSELF!

NOT like this:

Rectangle testRectangle = newRectangle(15, 13);
testRectangle.all();

As you can see, all() is called directly on the capitalized class name. Remember, static methods and variables deal with the entire class not a particular instance.

In upcoming lessons, we'll practice creating and using static variables and methods further, including integrating them into a Spark user interface when we add them to our To Do List application.


Example GitHub Repo for Rectangle Application

Terminology


  • Static: Instead of being related to an individual instance of a class, it's a type of variable or method meant for the class itself.

Tips


  • Static variable names should not be preceded by an m. Remember, m stands for member, referring to a particular instance of a class. Static variables hold information about the class itself, not a particular instance.

Additional Resources