2017-09-24

3: Where to Use Static Members Classes and Where to Use Singletons

<The previous article in this series | The table of contents of this series | The next article in this series>

Main Body START

When Static Members Classes Don't Do, We Can Use Singletons

About: Java Programming Language

Static Members Classes Are Handy in Some Cases When We Need Only One State per Class at a Time

-Hypothesizer

Static members classes are often handy when we need only one state per class at a time.

-Rebutter

What do you mean by 'static members classes'?

-Hypothesizer

I mean classes that have only static fields and static methods, like this.

@Java Source Code
public class Test1StaticMembersClass {
 private static String s_valueA = "initial value A";
 
 private Test1StaticMembersClass () {
 }
 
 public static void setValueA (String a_valueA) {
  s_valueA = a_valueA;
 }
 
 public static void printValueA () {
  System.out.println (String.format ("The value A is \"%s\".", s_valueA));
 }
}
-Rebutter

So, you don't mean nested classes.

-Hypothesizer

No.

With static members classes, we don't need to create class instances, and can use them like this.

@Java Source Code
public class Test1TestClass {
 public void test () {
  Test1StaticMembersClass.setValueA ("changed value A");
  Test1StaticMembersClass.printValueA ();
 }
}
-Rebutter

The static members class has only one state at a time, but the state can be changed with time.

-Hypothesizer

It's important that the whole system sees the correct single state at any time.

If the value is to be constant, we can also do like this with an instance field.

@Java Source Code
public class Test2InstanceMembersClass {
 private String i_valueA = "constant value A";
 
 public void printValueA () {
  System.out.println (String.format ("The value A is \"%s\".", i_valueA));
 }
}

public class Test2TestClass {
 public void test () {
  (new Test2InstanceMembersClass ()).printValueA ();
 }
}
-Rebutter

Although we can create multiple instances of 'Test2InstanceMembersClass', the value of any instance is guaranteed to be the single value because the field is private and doesn't have any setter method. However, we can't change the value.

-Hypothesizer

Besides, static members classes bring a benefit that there is only one instance of the value, which means no waste of memory space.

-Rebutter

In the example above, waste of memory space seems practically nothing, but there can be cases in which the states occupy large memory space.

-Hypothesizer

And initialization of the states may require much CPU resource.

-Rebutter

It may.

-Hypothesizer

We can have those benefits just by declaring fields to be static.

What to Be Aware of When We Extend Static Members Classes

-Hypothesizer

However, when we want to extend a static members class, we may face a dilemma. For example, we may want to overwrite the value of the field in the sub class.

-Rebutter

Well, I guess, what we want is to make different sub classes have different values, right?

-Hypothesizer

Yes. So, as sharing the same super class field among sub classes is meaningless, we will have to do so-called shadowing like this.

@Java Source Code
public abstract class Test3BaseStaticMembersClass {
 protected static String s_valueA = "initial value A";
 
 public static void setValueA (String a_valueA) {
  s_valueA = a_valueA;
 }
 
 public static void printValueA () {
  System.out.println (String.format ("The value A is \"%s\".", s_valueA));
 }
}

public class Test3ExtendedStaticMembersClass extends Test3BaseStaticMembersClass {
 protected static String s_valueA = "overwritten value A";
 
 private Test3ExtendedStaticMembersClass () {
 }
}
-Rebutter

Ah-ha . . .

-Hypothesizer

But when we use it like this, the result isn't what I want.

@Java Source Code
public class Test3TestClass {
 public void test () {
  Test3ExtendedStaticMembersClass.printValueA ();
 }
}
-Rebutter

That will be so, considering the rules of class member access resolution.

Let's Review the Rules of Class Member Access Resolution for Static Members

-Hypothesizer

We are talking about the rules that determine which class member will be accessed for a given expression, right?

-Rebutter

Yes. For example, for the expression, 's_valueA', in 'printValueA' above, will 's_valueA' of 'Test3BaseStaticMembersClass' be accessed or will 's_valueA' of 'Test3ExtendedStaticMembersClass' be accessed?

-Hypothesizer

I thought we would review the rules of class member access resolution thoroughly here, but that seems to become a too long digression. So, here, we will review the rules of class member access resolution only for static members (we will review the rules for any member in a future article).

-Rebutter

All right.

-Hypothesizer

First, we must see the qualification to the member name in the expression.

-Rebutter

What do you mean by 'qualification'?

-Hypothesizer

When the expression is 'Test3ExtendedStaticMembersClass.s_valueA', 'Test3ExtendedStaticMembersClass' is the qualification to the member name, 's_valueA'.

If we just say 's_valueA', the compiler or the runtime can't determine which 's_valueA'. So, there must be always a qualification.

-Rebutter

In the code above, the expression is just 's_valueA' without any qualification.

-Hypothesizer

In that case, we think that there is an implicit qualification. By bringing the implicit qualification to light, we can understand the rules reasonably.

-Rebutter

OK. So, first, we see the qualification to the member name in the expression, whether it is explicit or implicit. Then what?

-Hypothesizer

If the class of the qualification has a static member of that member name, the member will be accessed. If not, it's out of our scope here, for an instance member will be accessed or a compile error will occur.

-Rebutter

What do you mean by 'the class of the qualification'?

-Hypothesizer

When the qualification is a class, the class is it.

-Rebutter

I thought so.

-Hypothesizer

When the qualification is a variable, the variable type is it.

-Rebutter

Let's think of this example.

@Java Source Code
  Test3BaseStaticMembersClass l_object = new Test3ExtendedStaticMembersClass ();

For the variable, 'l_object', 'Test3BaseStaticMembersClass', not 'Test3ExtendedStaticMembersClass', is the variable type.

-Hypothesizer

Yes. We will call 'Test3ExtendedStaticMembersClass' an instance type.

-Rebutter

All right.

-Hypothesizer

When the qualification is an expression, the expression type is it. For example, for the qualification, '( (Test3BaseStaticMembersClass) new Test3ExtendedStaticMembersClass ())', 'Test3BaseStaticMembersClass' is it.

-Rebutter

Ah-ha. So, it's important to know that the class of the qualification isn't the instance type.

-Hypothesizer

Yes.

In short, I say, the expression type of the qualification is the class of the qualification, for any class or any variable is a kind of expression.

-Rebutter

So, what is left is "What is the implicit qualification?"

-Hypothesizer

When the expression is in any static context, the implicit qualification is the class in which the expression appears; when the expression is in any instance context, the implicit qualification is 'this'.

-Rebutter

The inside of a static method is a typical static context, and the inside of an instance method is a typical instance context, I presume?

-Hypothesizer

Yes. In other words, any place where 'this' can be used is an instance context; elsewhere is a static context.

-Rebutter

OK.

-Hypothesizer

So, to know which class member the expression, 's_valueA', in 'printValue' above will resolve to, we first recognize that the qualification is implicit; then we recognize that the context is static because the expression is in the static method; so we understand that the qualification is 'Test3BaseStaticMembersClass', which is the class in which the expression appears; then we understand that the class of qualification is 'Test3BaseStaticMembersClass'; finally we recognize that 'Test3BaseStaticMembersClass' has the static member of that name.

-Rebutter

So, 's_valueA' of 'Test3BaseStaticMembersClass' will be accessed, not of 'Test3ExtendedStaticMembersClass'.

-Hypothesizer

I can foresee some complaints as "Can't you just explain more simply in just one or a few sentences"? Well, I could if I intended to make an intuitive explanation that hides some exceptions untold. I tried to make an explanation that is always true without any exception.

-Rebutter

Fair enough.

Then, What Should We Do With Static Members Classes

-Hypothesizer

Then, what should we do with the 'test3' case? Well, one way will be to redefine 'printValueA' in the sub class.

-Rebutter

You will have to define an exact copy of 'printValueA' of the super class, which seems an ill-mannered code: you will have to change all the copies when you want to change 'printValueA'.

-Hypothesizer

I know. . . . Well, we can separate an important part to another method like this.

@Java Source Code
public abstract class Test4BaseStaticMembersClass {
 private static String s_valueA = "initial value A";
 
 protected static void printSpecifiedValue (String a_valueName, String a_value) {
  System.out.println (String.format ("The value %s is \"%s\".", a_valueName, a_value));
 }
 
 public static void printValueA () {
  printSpecifiedValue ("A", s_valueA);
 }
}

public class Test4ExtendedStaticMembersClass extends Test4BaseStaticMembersClass {
 private static String s_valueA = "overwritten value A";
 
 private Test4ExtendedStaticMembersClass () {
 }
 
 public static void printValueA () {
  printSpecifiedValue ("A", s_valueA);
 }
}

public class Test4TestClass {
 public void test () {
  Test4ExtendedStaticMembersClass.printValueA ();
 }
}
-Rebutter

That looks better, but still, having to make exact copies of 'printValueA' for all sub classes seem cumbersome.

On What Cases We Should Be Aware of?

-Hypothesizer

So, when members are to be overwritten, static members classes aren't optimal.

-Rebutter

That is an inaccurate expression. If overwritten members aren't referred to in the super class, usually from a method, there will be no problem.

-Hypothesizer

Ah, if 's_valueA' isn't accessed from 'printValueA', there will be no problem even if we do shadowing. So, when members that are referred to in the super class are to be overwritten, static members classes aren't optimal.

-Rebutter

That seems so.

There Are Singletons

-Hypothesizer

Then, what can we do?

-Rebutter

We won't particularly need to persist to use static members classes if maintaining only one state per class is the sole issue.

-Hypothesizer

Then what will we use? . . . Oh, yes, there are singletons.

-Rebutter

Of course, there are.

-Hypothesizer

We do like this.

@Java Source Code
public abstract class Test5BaseSingletonClass {
 private String i_valueA = "initial value A";
 
 public void setValueA (String a_valueA) {
  i_valueA = a_valueA;
 }
 
 public void printValueA () {
  System.out.println (String.format ("The value A is \"%s\".", i_valueA));
 }
}

public class Test5ExtendedSingletonClass extends Test5BaseSingletonClass {
 public static final Test5ExtendedSingletonClass instance = new Test5ExtendedSingletonClass ();
 
 private Test5ExtendedSingletonClass () {
  setValueA ("overwritten value A");
 }
}

public class Test5TestClass {
 public void test () {
  Test5ExtendedSingletonClass.instance.printValueA ();
 }
}
-Rebutter

That way, we don't need to do shadowing or copy methods.

-Hypothesizer

The sub class creates and disclose the single instance of itself as a public static final field, and makes the constructor private, prohibiting any further creation of its instances.

Main Body END

<The previous article in this series | The table of contents of this series | The next article in this series>