Misnomer: static Keyword

The keyword “static” is arguably a bad choice of naming.

If you look up “static” in a dictionary you find many meanings, none of which explains its use in programming languages. So why is it used in Java, and how is it misunderstood?

According to Merriam-Webster it has seven meanings. A common use of static means “standing or fixed in one place”. But that’s not what it means in programming.

Why static is used

It’s usually short for “static binding”. The other kind of binding is “dynamic binding”. Binding is the process of mapping one thing to another. For example an element “foo” needs to be mapped to the type that declares it. (see examples below)

There are two types of binding:

  • static: the compiler does early binding during compilation
  • dynamic: the runtime does late binding during execution

There is another distinction:

  • per instance: specific to each instance
  • per type: specific to the class or interface

Example of static binding, per instance

You could have a class A with a field foo and B extending A with another field of the same name. We say that B.foo is hiding A.foo. And whoever does this should be fired immediately. But the compiler still has to decide unambiguously which one is used.

public class A { int foo; }
public class B extends A { int foo; }

We say that foo is per instance, because every instance of A will have it’s own “foo”. But it is statically bound because the compiler looks as the type of the expression (i.e. variable), not the referenced object.

A a = new B();
a.foo; // bound to A.foo
((B)a).foo; // bound to B.foo

Example of static binding, per class

You could have a class A with a static variable “FOO”. It is per class, because by creating instances of A you do not get more constants, and it’s statically bound because the compiler already knows which one to use, even if you would hide FOO in a subclass (see above example).

public class A { public static final int FOO = 42; }

Static methods behave similarly to static variables. But you can use final to prevent anyone from hiding it.

Example of dynamic binding, per instance

You could have a class A with a method foo() and B extending A with another method of the same name. We say that B.foo() is overriding A.foo(). This is fine as long as foo() is intended to be overridden. You can use final to prevent any overrides of the method.

public class A { int foo() { return -1; } }
public class B extends A { @Override int foo() { return 1337; } }

Whenever you call foo() the compiler doesn’t care which implementation is used. The runtime will have to figure it out. And the runtime only looks at the actual type for the object, not the type of the expression (i.e. variable) referencing it.

It is per instance, because the method can use nonstatic fields and so a.foo() can give other results than b.foo(). It’s dynamically bound because the compiler doesn’t know which one it will be. The late binding is done during execution.

The annotation @Override is used so the compile can check you actually override something and so that other programmers can easily see that is overriding something.

Nestet classes

The Java programming language allows you to define a class within another class and you can use the keyword static for this.

There are two categories:

  • static nested classes
  • inner classes (nonstatic nested classes)

The binding to the type is never really dynamic, because it only exists once (although class loading is very dynamic in Java, as the class loaders to this). But inner classes get an extra field by the compiler to hold a reference to the instance to which the instance of the nested class is bound to at runtime.

public class A {
  public class B { }
}

a.new B() is basically the same as new B(a), if B was not nested and had a field A a, to reference some instance of A. That’s all there is to this. No magic involved.

Misconceptions

All this is confusing and leads to many misconceptions. Nonstatic fields are just as static as static fields! The compiler binds all of them early. There’s nothing dynamic about new A().foo or even new B().foo if foo is only declared in A.

A method that doesn’t have the static keyword, but is private or doesn’t override anything and is final isn’t really dynamic. There’s nothing to do at runtime, because the method only exists once. Such methods could not be overridden, so this can be optimised.

Other languages use “var” and “fun” or similar keywords to declare variables and functions. There it makes much more sense to use “class var” and “class fun” to indicate that they are per class, not per instance. But class int foo() seems a bit odd.

The same is true for nested classes, but “class class” might be even more confusing. In that case “class” and “inner class” would be better, but then a new keyword “inner” would have had to be reserved.

Many argue that static isn’t even needed and it is against the principles of OOP. Kotlin doesn’t even have a static keyword. Instead you can have companion objects, which have a single instance, which is per class. Scala uses a similar concept of companion objects.

Leave a Reply

Your email address will not be published. Required fields are marked *