Monday, June 15, 2009

Static Fields and Inheritance

Someone asked me recently to find out the real reason why the code from this thread fails. This is a fairly bad code, and not even a very good way to point out the problem. But the question is nonetheless interesting.

class Toto extends TotoParent{

final static Toto a = new Toto ("a");

public Toto(String a){
super(a);
}
}

import java.util.ArrayList;
import java.util.List;

public abstract class TotoParent {

static List list = new ArrayList();

public TotoParent(String a) {
list.add(a);
}

protected static List get() {
return list;

}
}

import org.junit.Test;
import static org.junit.Assert.*;

public class TotoTest {

@Test
public void testGet(){
assertEquals(1, Toto.get().size());
}
}
I am quite used to static initialization, and would have answered the same as the first answer in the thread:
"Get is static and associated with TotoParent, so that is the same as calling TotoParent.get().size()". I would have even thought that the compiler would compile the call Toto.get() to TotoParent.get(). But running javap, you can see it is still compiled as TotoParent.get(). So there is still a lookup done. This is why the first answer is actually not that correct.

The important bit here is that Toto is never initialized, even if we call Toto.get(). The java specs (invaluable reference) explains clearly that calling a static method not declared in the class does not initialize the class.

Calling Toto.get() is not exactly the same as calling TotoParent.get().
If TotoParent.get() called another TotoSuperParent.get():
Toto.get() -> TotoParent.get() -> TotoSuperParent.get()
We compile then later we change to make TotoParent have a specific implementation of get(). Toto will then be automatically aware of it, without even recompiling it.

http://java.sun.com/docs/books/jls/third_edition/html/execution.html
paragraph 12.4.1

No comments :

Post a Comment