Most Java developers will be familiar with polymorphism – we’ve all seen the example of the Dog
and Cat
classes inheriting from some abstract Animal class and having their say()
methods produce different results. But it’s still worthwhile to look at a few simple examples to reinforce the concepts.
First, we define a simple class with one instance method and one static method.
public class A
{
public String getName()
{
return "I am A";
}
public static String getStaticName()
{
return "Statically A!";
}
}
Then we extend that class with one that has identical method signatures.
public class B extends A
{
// Note: @Override only makes sense for instance methods.
// The annotation is not needed but makes for best practices, since if the
// method DOES NOT override a superclass, a compile-time error will be
// generated, limiting damage. (@Override was added in Java 1.5)
@Override
public String getName()
{
return "I am B";
}
public String onlyOnB()
{
return "Only available on B";
}
// Cannot @Override, generates a compile error. Instead, this methods
// `hides` the one in the super class.
public static String getStaticName()
{
return "Statically B!";
}
public static void main( String[] args )
{
A a = new A();
B b = new B();
A b_as_a = new B();
A b_as_a_copied_from_reference = b;
System.out.println(a.getName());
System.out.println(a.getStaticName() + "\n");
System.out.println(b.getName());
System.out.println(b.getStaticName() + "\n");
System.out.println(b_as_a.getName());
System.out.println(b_as_a.getStaticName() + "\n");
System.out.println(b_as_a_copied_from_reference.getName());
System.out.println(b_as_a_copied_from_reference.getStaticName() + "\n");
}
}
Sorry for the funky variable names in main()
, but camelCase just didn’t look good. Anyway, can you guess the output of the program? It’s actually quite interesting:
I am A
Statically A!
I am B
Statically B!
I am B
Statically A!
I am B
Statically A!
The first two are fairly straightforward, since for variables a
and b
, the declared type matches the instantiated type, so there can be no doubt. But what happens when the declared type does not match the instantiated type, as in the second two examples?
The short answer is this: When instance methods are invoked, they will always be called on the instantiated type, regardless of the declared type. When static or class methods are invoked, they will be called on the declared type.
Declared type vs. instantiated type
You can think of the declared type as a “window” into the actual instantiated type. This “window” provides a view as to what methods are available for invocation and provides these hints to the compiler. This is why you cannot call a method that exists on an instantiated type unless it has been declared or exists on the declared type. (The use of interfaces provides the best example of this)
When an instance method is invoked, the JVM will then determine the runtime type of the variable and then call the appropriate method on that object. This is why for the last two examples, the output was I am B
, even though the declared type was A
. This is what allows polymorphism to work in Java.
However, when a static or class method is invoked, it will always be invoked from the declared type, regardless of what the runtime or instance type is. This is because static methods are per-class rather than per-instance and thus the exact method invoked can be determined at compile time from the declared type. This is why for the last two examples, the output is from the the method defined on class A
, the declared type of the two variables.
What Sun has to say
Sun’s own tutorials on these subjects refers to this as hiding; that is, when a subclass static method has the same signature as one in a superclass, it hides it instead of overriding it. Override is a term reserved for instance methods only, and in fact, marking getStaticName()
with the annotation @Override
in class B
results in a compile-time error.
However, to me, it’s far simpler to just remember that static methods are always invoked on the declared type, while instance methods will be invoked on the instantiated type. This provides an easy way to remember how things work in the JVM.
Hi,
It was a wonderful and shot but very imp inormation.
Thanks
What if the static method needs to be called from inside the father class? like this:
public static class A {
public static String getStaticName () {
return getStaticNameP();
}
protected static String getStaticNameP() {
return “Statically A!”;
}
}
public static class B extends A {
public static String getStaticNameP() {
return “Statically B!”;
}
public static void main( String[] args ) {
System.out.println(B.getStaticName());
}
}
the output of this code is “Statically A!”
Imagine the public method is a save method you don’t want to override in the subclasses because it works just fine, but the filepath is class dependant (thus, a static method)
pak u kaung lahat