Java 'Static' keyword - Intro: How (not) to use it
Tags: java
Published: 2020-03-02 11:38:31

When do I use the static keyword? What does it do? Doesn't matter, the IDE is telling me where to add it!

With that attitude, my Main class ended up looking like this:


**Example 1**

    public class Main {
        static Contact contact = new Contact();
        static Input input = new Input();

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

    public static void showMenu() {
        int menuChoice;
        menuChoice = input.menuChoice();
        switch (menuChoice) {
            case 1: updateContact();
            default: break;
        }
    }

    static void updateContact() {
        updateContactFields();
        showContact();
    }

    static void updateContactFields() {
        // lots of update calls
    }

    static void showContact() {
        // Lots of prints
        showMenu();
    }
    }


It was a train-wreck for lots of reasons, but the static everything was causing problems I wasn't aware of.

**Bad move Jules**

I had priorities, and finding out what all of these (not so) generic keywords meant was low. Anyway, the program worked, and if I made a new method, IntelliJ told me that I couldn't reference a non-static method from a static context, so all of the methods became static.

In the back of my head however, there was something bothering me;
- The main method in a Java class must be static
- Every method I was using was directly or indirectly called through the main method
- Methods called by other classes didn't have to be static, but also could 
 be

So why was I adding this all of the time?

**Blocker**

Eventually, a combination of using recursion where loops were better and making my entire class static caused problems with state that I couldn't debug. Someone familiar with Java took a quick look and asked me if I knew what static did.

They described it, in Ruby terms, as making something a Class method or variable. I needed instance at most, but had unintentionally made everything global.

**Solution/Example 2**

    public class Main {

        Input input = new Input();
        ArrayList<Contact> contactList = new ArrayList<>();

    public static void main(String[] args) {
        Main main = new Main();
        main.showMenu();
    }

    public void showMenu() {
        boolean complete = false;
        while (!complete) {
            printMenuOptions();
            int userInput = input.menuChoice();
            switch (userInput) {
                case 1: {
                    newContact();
                    break;
                }
                case 2: {
                    updateExistingContact();
                    break;
                }
                case 3: {
                    showContact();
                    break;
                }
                default:
                    complete = true;
                    break;
            }
        }
    }

    void printMenuOptions() {
        // logs
    }

    void newContact() {
        contactList.add(new Contact(input.confirmInput("first name"), input.confirmInput("last name"), input.confirmInput("address"), input.confirmInput("phone number"), input.confirmInput("DOB in dd/mm/yyyy format"), input.confirmInput("email")));
    }

    void updateExistingContact() {
        if (checkForContacts()) {
            displayContacts();
            try {
                updateContactFields(contactList.get(Integer.parseInt(input.confirmInput("contact choice")) - 1));
            } catch (Exception e) {
                System.out.println("No such contact");
            }
        }
    }

    boolean checkForContacts() {
        if (contactList.size() == 0) {
            System.out.println("There are no contacts yet");
            return false;
        } else {
            return true;
        }
    }

    void updateContactFields(Contact contact) {
        // logs and update calls
    }

    void showContact() {
        if (checkForContacts()) {
            displayContacts();
            Contact contact = null;
            try {
                contact = contactList.get(input.contactChoice() - 1);
            } catch (Exception e) {
                System.out.println("No such contact");
                showMenu();
            }
            printContactDetails(contact);
        }
    }

    void printContactDetails(Contact contact) {
        // lots of logs
    }

    void displayContacts() {
        System.out.println("Please select a contact");
        for (int i = 0; i < contactList.size(); i++) {
            System.out.println(i + 1);
        }
    }
    }

(Note: there's extra functionality here too)

To avoid calling methods from the static method `main`, you just:
- Make an instance of the Main class in the main method
- Call any methods from that instance of Main (e.g. main.displayContacts())
- Now you only have to make things static where it's good practice

**When should things be static?**

I'm glad you asked: Let's say I have a list of contacts, and each of these contacts has several properties. In case they share a common property, the variable can be static to save memory.

    class Contact {
        String name;
        static String country = "Arstotzka";
        int age;
        // and so on
    }

If I have 10 Contact instances, the `name` variable will be assigned memory 10 times. On the other hand, `country` is static, so it's only given memory once, however many instances there are. It doesn't have to be static, but it's more efficient, memory-wise, to make it so.

Notes:
- It can still be changed, but it's less likely to be changed than non-static variables.
- Static variables must also be initialised with a value, as you are saying that the variable and its value is inherent to the class.

_Class Methods_

Class methods are used without instantiating the class, as was the case in Example 1. Main was never instantiated, so if I called any methods, they had to be class level instead of object level.

**Example 3**

    class Contact {
        String name;
        static String country = "Arstotzka";
        int age;
        // and so on

        static void updateCountry() {
            country = "Republia";
        }
    }

The static method in Example 3 updates the static variable `country`, so every instance of Contact will now have the string value 'Republia' instead of 'Arstotzka'.

**Recap**

- Static variables are class level variables, so are inherited by instances, and must be initialised before making an instance
- Static methods are class methods, and are called without the class being instantiated
- If you want to use instance methods, you need to instantiate your class (just now made the connection between instance variables/methods and instances of classes)

This is far from exhaustive, but is information that would have saved me a lot of time and pain if I had done the research earlier.

Let me know your thoughts, whether you think this is clear and useful, or I've missed something big and made some bad assertions!