Is it a bad idea to declare a final static method?
Solution 1
I don't consider it's bad practice to mark a static
method as final
.
As you found out, final
will prevent the method from being hidden by subclasses which is very good news imho.
I'm quite surprised by your statement:
Re-defining method() as final in Foo will disable the ability for Bar to hide it, and re-running main() will output:
in Foo
in Foo
No, marking the method as final
in Foo
will prevent Bar
from compiling. At least in Eclipse I'm getting:
Exception in thread "main" java.lang.Error: Unresolved compilation problem: Cannot override the final method from Foo
Also, I think people should always invoke static
method qualifying them with the class name even within the class itself:
class Foo
{
private static final void foo()
{
System.out.println("hollywood!");
}
public Foo()
{
foo(); // both compile
Foo.foo(); // but I prefer this one
}
}
Solution 2
Static methods are one of Java's most confusing features. Best practices are there to fix this, and making all static methods final
is one of these best practices!
The problem with static methods is that
- they are not class methods, but global functions prefixed with a classname
- it is strange that they are "inherited" to subclasses
- it is surprising that they cannot be overridden but hidden
- it is totally broken that they can be called with an instance as receiver
therefore you should
- always call them with their class as receiver
- always call them with the declaring class only as receiver
- always make them (or the declaring class)
final
and you should
- never call them with an instance as receiver
- never call them with a subclass of their declaring class as receiver
- never redefine them in subclasses
NB: the second version of you program should fails a compilation error. I presume your IDE is hiding this fact from you!
Solution 3
If I have a public static
method, then it's often already located in a so-called utility class with only static
methods. Self-explaining examples are StringUtil
, SqlUtil
, IOUtil
, etcetera. Those utility classes are by itselves already declared final
and supplied with a private
constructor. E.g.
public final class SomeUtil {
private SomeUtil() {
// Hide c'tor.
}
public static SomeObject doSomething(SomeObject argument1) {
// ...
}
public static SomeObject doSomethingElse(SomeObject argument1) {
// ...
}
}
This way you cannot override them.
If yours is not located in kind of an utility class, then I'd question the value of the public
modifier. Shouldn't it be private
? Else just move it out to some utility class. Do not clutter "normal" classes with public static
methods. This way you also don't need to mark them final
.
Another case is a kind of abstract factory class, which returns concrete implementations of self through a public static
method. In such case it would perfectly make sense to mark the method final
, you don't want the concrete implementations be able to override the method.
Solution 4
Usually with utility classes - classes with only static methods - it is undesirable to use inheritence. for this reason you may want to define the class as final to prevent other classes extending it. This would negate putting final modifiers on your utility class methods.
Solution 5
The code does not compile:
Test.java:8: method() in Bar cannot override method() in Foo; overridden method is static final public static void method() {
The message is misleading since a static method can, by definition, never be overridden.
I do the following when coding (not 100% all the time, but nothing here is "wrong":
(The first set of "rules" are done for most things - some special cases are covered after)
- create an interface
- create an abstract class that implements the interface
- create concrete classes that extend the abstract class
- create concrete classes that implements the interface but do not extend the abstract class
- always, if possible, make all variables/constants/parameters of the interface
Since an interface cannot have static methods you don't wind up with the issue. If you are going to make static methods in the abstract class or concrete classes they must be private, then there is no way to try to override them.
Special cases:
Utility classes (classes with all static methods):
- declare the class as final
- give it a private constructor to prevent accidental creation
If you want to have a static method in a concrete or abstract class that is not private you probably want to instead create a utility class instead.
Value classes (a class that is very specialized to essentially hold data, like java.awt.Point where it is pretty much holding x and y values):
- no need to create an interface
- no need to create an abstract class
- class should be final
- non-private static methods are OK, especially for construction as you may want to perform caching.
If you follow the above advice you will wind up with pretty flexible code that also has fairly clean separation of responsibilities.
An example value class is this Location class:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class Location
implements Comparable<Location>
{
// should really use weak references here to help out with garbage collection
private static final Map<Integer, Map<Integer, Location>> locations;
private final int row;
private final int col;
static
{
locations = new HashMap<Integer, Map<Integer, Location>>();
}
private Location(final int r,
final int c)
{
if(r < 0)
{
throw new IllegalArgumentException("r must be >= 0, was: " + r);
}
if(c < 0)
{
throw new IllegalArgumentException("c must be >= 0, was: " + c);
}
row = r;
col = c;
}
public int getRow()
{
return (row);
}
public int getCol()
{
return (col);
}
// this ensures that only one location is created for each row/col pair... could not
// do that if the constructor was not private.
public static Location fromRowCol(final int row,
final int col)
{
Location location;
Map<Integer, Location> forRow;
if(row < 0)
{
throw new IllegalArgumentException("row must be >= 0, was: " + row);
}
if(col < 0)
{
throw new IllegalArgumentException("col must be >= 0, was: " + col);
}
forRow = locations.get(row);
if(forRow == null)
{
forRow = new HashMap<Integer, Location>(col);
locations.put(row, forRow);
}
location = forRow.get(col);
if(location == null)
{
location = new Location(row, col);
forRow.put(col, location);
}
return (location);
}
private static void ensureCapacity(final List<?> list,
final int size)
{
while(list.size() <= size)
{
list.add(null);
}
}
@Override
public int hashCode()
{
// should think up a better way to do this...
return (row * col);
}
@Override
public boolean equals(final Object obj)
{
final Location other;
if(obj == null)
{
return false;
}
if(getClass() != obj.getClass())
{
return false;
}
other = (Location)obj;
if(row != other.row)
{
return false;
}
if(col != other.col)
{
return false;
}
return true;
}
@Override
public String toString()
{
return ("[" + row + ", " + col + "]");
}
public int compareTo(final Location other)
{
final int val;
if(row == other.row)
{
val = col - other.col;
}
else
{
val = row - other.row;
}
return (val);
}
}
brasskazoo
I am a Software and Cloud Solutions Engineer with a passion for building quality and automation into the development, testing and deployment lifecycles. Currently working with a large AWS hybrid-cloud integration project, including VPC architecting and solution design, serverless applications and shifting applications to EC2. Primarily I work with terraform, node.js, react and java, with side projects currently in react-native and GraphQL for cross-platform mobile applications. Agile, DevOps culture, Code Quality and Continuous Integration are cornerstones of my development efforts.
Updated on July 09, 2022Comments
-
brasskazoo almost 2 years
I understand that in this code:
class Foo { public static void method() { System.out.println("in Foo"); } } class Bar extends Foo { public static void method() { System.out.println("in Bar"); } }
.. the static method in
Bar
'hides' the static method declared inFoo
, as opposed to overriding it in the polymorphism sense.class Test { public static void main(String[] args) { Foo.method(); Bar.method(); } }
...will output:
in Foo
in BarRe-defining
method()
asfinal
inFoo
will disable the ability forBar
to hide it, and re-runningmain()
will output:in Foo
in Foo(Edit: Compilation fails when you mark the method as
final
, and only runs again when I removeBar.method()
)Is it considered bad practice to declare static methods as
final
, if it stops subclasses from intentionally or inadvertantly re-defining the method?(this is a good explanation of what the behaviour of using
final
is..) -
brasskazoo over 14 yearsGood point - one of my refactoring goals is to move static methods to a utility class, marking that class as final is a good idea.
-
brasskazoo over 14 yearsVery nice answer! By the way, the second version does indeed fail compilation in my IDE. Edited the question to clarify this.
-
ante.sabo almost 13 yearsare you absolutely sure about: always make them (or the declaring class) final - if you make declaring class final, automatically all public methods will be final, but does that applies also for STATIC methods?
-
Sandeep Chauhan over 12 yearsIf the class is final it cannot be subclasses, so it follows that all methods are final as well. For static method this means there cannot be a subclass method that hides them, which is equivalent to making the method final but not the class.
-
Mike Samuel almost 12 yearsThe first Java warning in Eclipse is "Non-static access to static member". Enable that, and you'll be warned whenever you accidentally use a static method with an instance receiver.
-
ZhongYu almost 11 yearsyes, we should make a habit of marking all public static methods as 'final'.
-
Ankit about 9 years@akuhn very nice answer... but i do not understand what do you mean by "always call them with their class as receiver" "always call them with the declaring class only as receiver"... What is the difference between these two points?... and what exactly "class as receiver" means?
-
Matt Keeble about 8 yearsI think this should be marked as the correct answer personally. This is exactly what I would have suggested. Finalise the class, not the individual method(s).
-
Adowrath about 7 years@Ankit The receiver is, simply said, what is on the left side of the . operator when calling a function.
-
Eugene about 6 years@GregoryPakosz so you are saying that
Unresolved compilation problem: Cannot override the final method from Foo
makes sense? For non overridable method to begin with? -
Eugene about 6 years@MattKeeble this does not matter, there is the optional to make it final and we should judge from there. Making it finals does make sense, it's just that compiler throws a very misleading error message in this case