<The previous article in this series | The table of contents of this series | The next article in this series>
When Static Members Classes Don't Do, We Can Use Singletons
About: Java Programming Language
Static members classes are often handy when we need only one state per class at a time.
What do you mean by 'static members classes'?
I mean classes that have only static fields and static methods, like this.
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));
}
}
So, you don't mean nested classes.
No.
With static members classes, we don't need to create class instances, and can use them like this.
public class Test1TestClass {
public void test () {
Test1StaticMembersClass.setValueA ("changed value A");
Test1StaticMembersClass.printValueA ();
}
}
The static members class has only one state at a time, but the state can be changed with time.
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.
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 ();
}
}
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.
Besides, static members classes bring a benefit that there is only one instance of the value, which means no waste of memory space.
In the example above, waste of memory space seems practically nothing, but there can be cases in which the states occupy large memory space.
And initialization of the states may require much CPU resource.
It may.
We can have those benefits just by declaring fields to be static.
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.
Well, I guess, what we want is to make different sub classes have different values, right?
Yes. So, as sharing the same super class field among sub classes is meaningless, we will have to do so-called shadowing like this.
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 () {
}
}
Ah-ha . . .
But when we use it like this, the result isn't what I want.
public class Test3TestClass {
public void test () {
Test3ExtendedStaticMembersClass.printValueA ();
}
}
That will be so, considering the rules of class member access resolution.
We are talking about the rules that determine which class member will be accessed for a given expression, right?
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?
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).
All right.
First, we must see the qualification to the member name in the expression.
What do you mean by 'qualification'?
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.
In the code above, the expression is just 's_valueA' without any qualification.
In that case, we think that there is an implicit qualification. By bringing the implicit qualification to light, we can understand the rules reasonably.
OK. So, first, we see the qualification to the member name in the expression, whether it is explicit or implicit. Then what?
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.
What do you mean by 'the class of the qualification'?
When the qualification is a class, the class is it.
I thought so.
When the qualification is a variable, the variable type is it.
Let's think of this example.
Test3BaseStaticMembersClass l_object = new Test3ExtendedStaticMembersClass ();
For the variable, 'l_object', 'Test3BaseStaticMembersClass', not 'Test3ExtendedStaticMembersClass', is the variable type.
Yes. We will call 'Test3ExtendedStaticMembersClass' an instance type.
All right.
When the qualification is an expression, the expression type is it. For example, for the qualification, '( (Test3BaseStaticMembersClass) new Test3ExtendedStaticMembersClass ())', 'Test3BaseStaticMembersClass' is it.
Ah-ha. So, it's important to know that the class of the qualification isn't the instance type.
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.
So, what is left is "What is the implicit qualification?"
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'.
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?
Yes. In other words, any place where 'this' can be used is an instance context; elsewhere is a static context.
OK.
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.
So, 's_valueA' of 'Test3BaseStaticMembersClass' will be accessed, not of 'Test3ExtendedStaticMembersClass'.
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.
Fair enough.
Then, what should we do with the 'test3' case? Well, one way will be to redefine 'printValueA' in the sub class.
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'.
I know. . . . Well, we can separate an important part to another method like this.
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 ();
}
}
That looks better, but still, having to make exact copies of 'printValueA' for all sub classes seem cumbersome.
So, when members are to be overwritten, static members classes aren't optimal.
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.
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.
That seems so.
Then, what can we do?
We won't particularly need to persist to use static members classes if maintaining only one state per class is the sole issue.
Then what will we use? . . . Oh, yes, there are singletons.
Of course, there are.
We do like this.
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 ();
}
}
That way, we don't need to do shadowing or copy methods.
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.
<The previous article in this series | The table of contents of this series | The next article in this series>