Java – Why Declaring Public Static Variables is Bad

javaobject-oriented

GOAL: to have a main class which has some global variables which need to be modified by other classes.

I have a class with a structure like below

    public class ClassName {
       public static int variable;

       public void meth1(){
       }    
       ...
   }

Then I have other classes which need to use and update that variable. The structure of these classes is like this:

public class ClassName2 {
     public void meth(){
           ClassName1.variable = 4;
      }
}

QUESTION: how is bad this approach? I was asking myself what happens if I want to use ClassName2 in another project? And answering to myself I've discovered that I've created a dependency of 'ClassName2' from 'ClassName1'
How can I avoid this dependency from 'ClassName1' ?

Best Answer

Global variables are never necessary unless you are interfacing with existing code that imposes unsuitable constraints.

One alternative is to make the dependency explicit:

1. Create a class that describes the purpose of this shared data. Without knowing your problem domain, I'm going to call it a Counter.

class Counter {
  private int value = 0;

  public void incrementBy(int x) {
    if (x < 0)
      throw IllegalArgumentException("must increment by non-negative value");
    value += x;
  }

  public int get() { return value; }
}

Note that wrapping the variable through accessor functions allows us to impose checks and constraints. This lack of checks is one major problem with exposing variables directly.

2. Use some dependency injection mechanism to supply a Counter instance to all classes that need it. For example, we can use constructor injection:

public class ClassName2 {
  private Counter counter;

  public ClassName2(Counter counter) {
    this.counter = counter;
  }

  public void meth(){
    counter.incrementBy(4);
  }
}

The counter is created in some initialization code (like your main() function) and then provided when the dependent classes are instantiated:

Counter counter = new Counter();
ClassName  instance  = new ClassName(counter);
ClassName2 instance2 = new ClassName2(counter);

All global variables have a similar instantiation, but it's just done implicitly during class loading. By managing the initialization ourselves we gain a lot of flexibility:

  • we can supply a different Counter class during testing.
  • we are able use multiple Counter instances if different parts of the code are not supposed to share a Counter, for example when the original ClassName functionality is reused in a different context.

If you cannot make these dependencies explicit, at least encapsulate the variable using static methods in a similar fashion.