Java Forum / General / March 2007
Static methods overridden !!
Ravi - 25 Mar 2007 17:55 GMT Hi,
Consider the following piece of code
class AnimalTest { public static void saySomething() { System.out.println(" AnimalTest!"); } }
class CowTest extends AnimalTest { public static void saySomething1() { System.out.println(" CowTest!"); }
public static void main(String[] args) { AnimalTest[] animals = { new AnimalTest(), new CowTest() }; for (AnimalTest a : animals) { a.saySomething(); } new CowTest().saySomething(); } }
Interestingly it prints out AnimalTest! thrice. I am wondering how did it compile as CowTest doesn't have the method saySomething. Does the static method got carried over to the subclass??
Regards, Ravi
Daniel Pitts - 25 Mar 2007 18:36 GMT > Hi, > [quoted text clipped - 28 lines] > Regards, > Ravi Static methods are Class level, so it doesn't matter WHAT object type you have.
As a matter of fact, the following WILL work.
CowTest ct = null; ct.saySomething();
Static calls are really replaced by CowTest.saySomething() (which it inherits from AnimalTest)
Hope this helps, Daniel.
Ravi - 25 Mar 2007 19:10 GMT > Static calls are really replaced by CowTest.saySomething() (which it > inherits from AnimalTest) Yep, I understand that Static method calls are at class level than at object level. But, are you trying to say that static methods will be inherited but not overridden? This is strange and confusing. Can you please try n explain from JVM point of view? Thanks.
Eric Sosman - 25 Mar 2007 20:20 GMT >> Static calls are really replaced by CowTest.saySomething() (which it >> inherits from AnimalTest) [quoted text clipped - 3 lines] > inherited but not overridden? This is strange and confusing. Can you > please try n explain from JVM point of view? Thanks. Will you accept an explanation that's not from the JVM point of view, but from the Java language point of view?
Static methods can't be overridden, because there's no polymorphism involved. For instance methods, the choice of which method is called depends on the actual class of the object that is referenced -- on the `this', if you like:
Object thing = "Hello, world!"; System.out.println(thing.toString()); // String's method thing = new File("forty.two"); System.out.println(thing.toString()); // File's method
But with static methods there is no `this' to determine which method to call. You write Classname.doit(42), and as expected you call the static doit() method of the class Classname. Observe that in this form there is *no* object instance to govern the selection of different doit() methods; you will always and only get the doit() of Classname, and not some other class' doit().
The confusion arises when you don't write the class name, but instead use a reference variable: thing.doit(42). Many people find it odd that Java even permits this form, and some go so far as to call it a flaw in the language (I'm not enough of a connoisseur to have an opinion), but for reasons good or bad the form is legal. But it still doesn't mean that Java checks the class of the object `thing' refers to to decide which doit() to call; Java calls the doit() method of the class that matches the declared type of the `thing' reference. Indeed, as Daniel Pitts pointed out, `thing' need not even be a valid object reference at all:
Classname thing = null; thing.doit(42); // Classname's method
You can explore this a little more by making a few simple alterations to your original code sample, coming up with something like
class Animal {
public static void main(String[] unused) { Animal[] zoo = { new Animal(), new Dog(), new Beagle(), null }; for (Animal a : zoo) { System.out.println(a.getName()); } }
static String getName() { return "Animal"; } } class Dog extends Animal { static String getName() { return "Dog"; } } class Beagle extends Dog { static String getName() { return "Beagle"; } }
What output do you expect? What would you expect if the final three `static' keywords were removed? Make your predictions, then try it both ways and see.
 Signature Eric Sosman esosman@acm-dot-org.invalid
Ravi - 25 Mar 2007 20:49 GMT Thanks Eric for elaborate explanation on static method calls.
> Will you accept an explanation that's not from the JVM > point of view, but from the Java language point of view? Not a problem :)
>Java calls the doit() method of the class > that matches the declared type of the `thing' reference. Well, the class doesn't have the method at all ! Please refer the original code again. CowTest class doesn't have saySomething method at all . It has saySomething1 (1 (one) extra in the end, may be I should have made this more clearly differentiating). I am not referring the objects here and I am clear on the fact that objects have nothing to do with invocation of static methods.
you can replace new CowTest().saySomething(); with CowTest.saySomething(); if it makes things simpler.
Clearly saySomething comes from super class. This is confusing for me.
Regards, Ravi
Lew - 25 Mar 2007 21:24 GMT > Clearly saySomething comes from super class. This is confusing for > me. The method-resolution mechanism moves up the inheritance hierarchy from the compile-time type to look for the first matching method at runtime. Since CowTest "is-an" AnimalTest, the invocation from CowTest can find the method in AnimalTest.
However, if CowTest did have a matching static method, it wouldn't override the superclass static method, it would hide the superclass static method. There is no polymorphism of static methods.
AnimalTest.saySomething() will never invoke CowTest.saySomething().
-- Lew
Joshua Cranmer - 25 Mar 2007 21:32 GMT > Well, the class doesn't have the method at all ! Please refer the > original code again. CowTest class doesn't have saySomething method at [quoted text clipped - 5 lines] > you can replace new CowTest().saySomething(); with > CowTest.saySomething(); if it makes things simpler. Yes.
> Clearly saySomething comes from super class. This is confusing for > me. > > Regards, > Ravi What is going in is that the compiler is determining /at compile time/ what the method to be called is, based on the declaration of the variable. For example, this will not compile:
Object foo = new AnimalTest(); foo.saySomething();
because method Object does not have a method 'saySomething'. The compiler is using the declaration of the variable (Object in my example, AnimalTest in yours) to determine what static method to call. At no point does the actual type come into play. For example, this:
((AnimalTest)new Object()).saySomething();
will compile and is equivalent to this:
AnimalTest.saySomething();
even though it would throw a ClassCastException if it were an instance method, like so:
class Foo { public void bar() {} public static void main(String[] args) { ((Foo)new Object()).bar(); } }
Hope this helps.
Lew - 25 Mar 2007 21:39 GMT Ravi wrote:
>> Well, the class doesn't have the method at all ! Please refer the >> original code again. CowTest class doesn't have saySomething method at >> all . It has saySomething1 (1 (one) extra in the end, may be I should >> have made this more clearly differentiating). I am not referring the >> objects here and I am clear on the fact that objects have nothing to >> do with invocation of static methods. The method-resolution mechanism moves up the inheritance hierarchy from the compile-time type to look for the first matching method. Since CowTest "is-an" AnimalTest, the invocation from CowTest can find the method in AnimalTest.
However, if CowTest did have a matching static method, it wouldn't override the superclass static method, it would hide the superclass static method. There is no polymorphism of static methods.
AnimalTest.saySomething() will never invoke CowTest.saySomething().
-- Lew
Ravi - 26 Mar 2007 03:50 GMT > The method-resolution mechanism moves up the inheritance hierarchy from the > compile-time type to look for the first matching method. Since CowTest "is-an" [quoted text clipped - 3 lines] > the superclass static method, it would hide the superclass static method. > There is no polymorphism of static methods. My understanding of overriding is as follows. (Not polymorphism , just sticking to overriding)
Class Super { public void method1(){} } Class DerivedOne extends Super { } Class DerivedTwo extends Super { public void method1(){} }
public Class OverrideDemo { public static void main(String a[]) { new DerivedOne().method1(); //calls the Super class method1() new DerivedTwo().method1(); //calls the DerivedTwo class method1() } }
I am not talking about polymorphism here, just overriding. If you observe carefully, then the behavioiur of statis methods is exactly similar. Assume that the method1 is static. DerivedOne.method1() ; //will call Super static method method1. DerivedTwo.method2(); //will call DerivedTwo static method method1.
Technically speaking static methods ARE OVERRIDDEN, not at the object level ofcourse at the class level as invocation of statics is not object dependent. Also, we cannot take the advantage of polymorphism in case of the static methods but thats not the point. Static methods are overridden.
Does my argument make sense?
Regards, Ravi
Michael Rauscher - 26 Mar 2007 08:53 GMT > Technically speaking static methods ARE OVERRIDDEN, not at the object > level ofcourse at the class level as invocation of statics is not > object dependent. Also, we cannot take the advantage of polymorphism > in case of the static methods but thats not the point. Static methods > are overridden. Speaking in terms of the JLS, static methods are hidden, not overriden. See [1] for details.
Bye Michael
[1] <http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8>
Chris Uppal - 26 Mar 2007 09:35 GMT > I am not talking about polymorphism here, just overriding. In Java overriding /is/ polymorphism. That's to say, the only use of the word "overriding" that's allowed (if you want to talk be understood by other Java programmers) is to mean the instance-side re-implementation of inherited methods by subclasses (or in implementations of interfaces).
As you know, Java's static methods don't work like that, so the word "overriding" is simply not applicable here.
Probably the best way to think about what's happening is to take it in steps.
[WARNING: the following is a simplified view, and is inaccurate in small ways that I'll explain at the end of this post]
First off, as you also know, you can invoke any (public) static method from any place by using the syntax: fully.qualified.ClassName.staticMethod();
When it comes right down to it -- i.e. in terms of how the compiler treats static methods -- that's the /only/ way to invoke a static method. But the compiler does allow a number of convenient abbreviations.
One, which has already been discussed, is the ill-conceived idea that you can replace the name of the class by some expression with the (statically determined) type which is the name of the class.
Another abbreviation is that if you have an import fully.qualified.ClassName; or fully.qualified.*; statement in your Java source, then you can refer to the class by its short name, and thus invoke the static method by the shorter expression: ClassName.staticMethod();
Yet another abbreviation is that if the method name is "in scope" then you don't need the class name at all, you can just write: staticMethod();
Now, the concept of being in scope is interesting. It applies everywhere within the body of the class where the static method is defined. And it /also/ applies throughout the bodies of any subclasses of that class. Hence, within a subclass, you can also write staticMethod(); and the name will be resolved to: fully.qualified.ClassName.staticMethod(); at compile time (remember the above warning -- that assertion is technically incorrect, although it does capture the /intended/ idea of what's happening).
A further refinement of the idea of scoping is that if you have a class, Super, which defines a static method, aMethod(), and you have a subclass Sub, which does /not/ define a method of the same name and signature, then you can invoke Super.aMethod() with the method call expression: Sub.aMethod(); And "aMethod" is resolved in the scope defined by Sub, which includes Super.aMethod().
You /can/ call that "inheritance" if you like -- it's certainly not an abuse of the word -- but it's important to realises that what is being inherited is the just the scope in which names are resolved (at compile time). The kind of inheritance we see here is completely different from the kind of polymorphic, OO, inheritance which is used for (non-private, non-final) instance methods. But there is no overriding involved at all (by definition of the word "override" as it is used in Java).
All OK so far ? I hope so, because it's a reasonably simple picture, and it does reflect how Java is intended to work and how we (as programmers) are intended to think about it. If you aren't interested in details, then stop reading now, because the rest of this post will just add confusion without making you better able to understand ordinary Java code.
Right, I said that the above was technically inaccurate; here's how. I said that the compiler statically fills in the name of the class when it expands the abbreviation. That's true, but there are rules about /which/ class name it fills in. You might expect it to look to see what class defines the method (which it has to do anyway) and use the name of that class to expand the abbreviation. In fact, it doesn't do that (although there were bugs in the javac compiler in this area as late as JDK 1.3). When a programmer writes: staticMethod(); the compiler uses the class where the method call is written to begin resolving the name "staticMethod()", it will find the definition of that method in some superclass, but it is not allowed to expand the abbreviation by naming that superclass explicitly -- it is required (by the JLS) to expand it out to the name of the class where the lookup /began/. In this case the name of the class where the code which calls staticMethod() is defined. For instance, if you have:
class fully.qualified.Super { static void aMethod() {} } class fully.qualified.Sub extends fully.qualified.Super { void someCode() { aMethod(); } } class my.Class extends fully.qualified.Sub { }
and you create an instance of class my.Class, and call the (instance-side) someCode() method that in inherits from fully.qualified.Sub, then the bytecode which are executed will contain the equivalent of the Java code (after expansion by the compiler): fully.qualified.Sub.aMethod(); even though there is no such method as fully.qualified.Sub.aMethod() ! That is surprising because it means that aMethod() is /actually/ resolved, at runtime, by a search up the inheritance hierarchy[*], despite the fact that Java's static classes are, according the language design, always resolved /statically/. The is room for debate about why there is this odd anomaly in the specification. One hypothesis is that the language designers were just confused about static methods, and didn't really have a clear idea of what they were trying to achieve. Another hypothesis is that it's all about supporting binary compatibility[**] as classes evolve independently or each other. My own opinion is that there's a bit of truth in both hypotheses, but that the second one is a bit more true than the first ;-)
-- chris
[*] Obviously the JVM will optimise out that search -- there's no time penalty at runtime.
[**] There is a large section of the JLS which is all about allowing class files to work correctly even if they have been compiled against different versions of the other classes they mention. Or, if not always /correctly/, then at least with clearly defined behaviour, requirements, and guarantees. See the JLS for details.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|