DEV Community

Mark Rubin
Mark Rubin

Posted on

How To Get Out Of Main And Reduce Static

This is part of a brief series on Good Habits For Java Programmers.

In the beginning...

When you write your first program in Java, you encounter the need to have a method with the signature

public static void main(String args[])
Enter fullscreen mode Exit fullscreen mode

that serves as the entry point for your program. When you run your program, somehow the Java runtime finds this magic entry point, calls into, and you're off to the races. You'll have to house this method in a class, because that's just how it works.

Then, whatever you learn next, you'll probably be reading and writing short programs that exercise the new Java language bits inside that public static void main method. Maybe you're learning how to iterate with a for loop, and so read or write a little program to sum up all the evens between 0 and 10.

public static void main(String args[]) {
    int sumOfEvens = 0;
    for (int even = 0; even <= 10; even += 2) {
        sumOfEvens += even;
    }
    System.out.println("The sum of evens from 0 to 10 is " + sumOfEvens);
}
Enter fullscreen mode Exit fullscreen mode

It makes sense to keep that code in main from a teaching and learning standpoint: it's the simplest place to put the code, and by introducing no additional complexity, the instructor and you get to focus on the other concepts being taught -- say, how a for loop works or how to use a local variable (sumOfEvens) or how the algorithm works (adding 2 each time takes us to the next even). I have no problem with this teaching methodology. In fact, I think it's a good idea to leave you to code in main while you focus on other things.

main is simply an entry point. It is not for modeling your data or problem space.

But let's move forward some number of weeks in your learning. You're starting to learn how to use classes to model a domain, and your assignment is to create a program that asks you to hardcode the length of the side of a square, and print out the area and the perimeter. So you create something like this:

public class Square {
    public static void main(String args[]) {
        float sideLength = 4.0;
        System.out.println("The area of the square is " + sideLength * sideLength);
        System.out.println("The perimiter of the square is " + 4 * sideLength);
    }
}
Enter fullscreen mode Exit fullscreen mode

This is a good start. It does what's asked. And it's somewhat nice that the class is named Square. The name of the class tells me that we're doing something related to a Square in this class, but we're not really doing much modeling of a Square here. There's no representation of a side's length in the Square class and there are no methods that we'd expect on a Square class. We call the class Square, but since all of the logic for the squarey things we do live in main, there actually isn't much that's squarey about this class, other than that the magic Java program entry point, main, is packed with a series steps that relate to doing some operations on a square.

Let's dwell on this. The main method is not the best place to put your algorithms or to model the problem domain you're modeling: the need for a main comes from a contract between the Java runtime and you about how to start your program. That's it. It's how the Java runtime finds a place to start your program, and that is its primary role. It's not for modeling your problem space or writing interesting algorithms: that modeling and those interesting algorithms belong in the distinctive methods and and fields of your classes. Treat main solely as an entry point for the Java runtime to find, and put interesting work into your class methods.

Don't get stuck in the static trap

So you learn about class methods and class fields, and you rewrite your Square class:

public class Square {
    private static float sideLength = 0.0f;

    public static void main(String args[]) {
        setSideLength(3.2f);
        System.out.println("The area of the square is " + getArea());
        System.out.println("The perimiter of the square is " + getPerimeter());
    }

    public static void setSideLength(float length) {
        sideLength = length;
    }

    public static float getArea() {
        return sideLength * sideLength;
    }

    public static float getPerimeter() {
        return 4 * sideLength;
    }
}
Enter fullscreen mode Exit fullscreen mode

This is an improvement. I can see more why this class is named Square: it stores a side length, and it has methods to calculate the area and perimeter of a square, using the correct algorithms for them based on a square's side's length.

But there's something funny here. Why is everything static? The field sideLength is static, and so are getArea and getPerimeter? In my experience, this is not so much a conscious choice, but a carry over from the fact that main is static. main has to be static because that's the contract with the Java runtime. And students get stuck then figuring out how to not carry on that static. They find that they can't call directly a method named setSideLength from their static main unless setSideLength is static, too. And then a static setSideLength can't set a value on that field sideLength unless it's static.

So there are two big issues here. The first is whether we want all these to be static. We don't, and I'll explain why. But more than that, the bigger conceptual issue is that it's not that students have made a choice I disagree with to make the field and the methods of Square static: it's that students feel that they are forced into it by their static main.

There's a big difference between static fields and non-static (instance or member) fields and a big difference between static methods and non-static (instance or member) methods. The difference I want to focus on for now is that a static field is shared among all instances of Squares, where each instance of Square has its own private copy of non-static fields. Conceptually, do we want to make it so that all Squares have the side length 3.2 when I set that value, or that just a particular Square has a side length of 3.2? Or put more broadly, do we want the Square class to model squares as if they all share the same side length, or do we want to allow it to model that different squares have different side lengths? The second, for sure. In that case, we don't want sideLength to be static.

Okay, so we don't want that field to be static, but as I said, most students don't choose to make it static: they feel forced into it because of the call to setSideLength in their public void static main, which itself is seemingly forced into being static

The solution is to create an instance of Square, and call a method on that:

public class Square {
    private float sideLength = 0.0f;

    public static void main(String args[]) {
        Square square = new Square();
        square.setSideLength(3.2f);
        System.out.println("The area of the square is " + square.getArea());
        System.out.println("The perimiter of the square is " + square.getPerimeter());
    }

    public void setSideLength(float length) {
        sideLength = length;
    }

    public float getArea() {
        return sideLength * sideLength;
    }

    public float getPerimeter() {
        return 4 * sideLength;
    }
}
Enter fullscreen mode Exit fullscreen mode

Much better! We now have made sideLength a non-static, instance field, and we access it through non-static, instance members. We should probably add a constructor to Square that takes an argument for the side length, too, but let's leave it at this.

Summary

There are two lessons here I want to summarize:

  • It's very easy to feel stuck having to make everything in a class static because you start calls to it from a static method, public static void main. But you don't have to be stuck: within your main, you can create an instance of your class and then make instance method calls on that instance. Those instance methods can access instance fields. If you want to make a field or method on your class static because that's appropriate, too, then great! But you don't have to.
  • public static void main serves as a rendezvous point between the Java runtime and your program. It is a contract between you and Java about how to start your program. That's it. It's not the place to put parts of your modeling or algorithms specific to your classes and their desired behaviors.

Here's a related post about where main should live.

Top comments (0)